@capgo/capacitor-uploader 8.1.20 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -13
- package/android/src/main/java/ee/forgr/capacitor/uploader/Uploader.java +43 -6
- package/android/src/main/java/ee/forgr/capacitor/uploader/UploaderPlugin.java +48 -15
- package/dist/docs.json +74 -11
- package/dist/esm/definitions.d.ts +58 -2
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +1 -0
- package/dist/esm/web.js +45 -10
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +45 -10
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +45 -10
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/UploaderPlugin/Uploader.swift +90 -30
- package/ios/Sources/UploaderPlugin/UploaderPlugin.swift +30 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -142,6 +142,25 @@ uploadToCustomServer(filePath, serverUrl);
|
|
|
142
142
|
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
+
### Example multi-file multipart upload
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { Uploader } from '@capgo/capacitor-uploader';
|
|
149
|
+
|
|
150
|
+
const { id } = await Uploader.startUpload({
|
|
151
|
+
serverUrl: 'https://api.example.com/upload',
|
|
152
|
+
method: 'POST',
|
|
153
|
+
uploadType: 'multipart',
|
|
154
|
+
files: [
|
|
155
|
+
{ filePath: 'file:///...photo1.jpg', fieldName: 'images[]', mimeType: 'image/jpeg' },
|
|
156
|
+
{ filePath: 'file:///...photo2.jpg', fieldName: 'images[]', mimeType: 'image/jpeg' },
|
|
157
|
+
],
|
|
158
|
+
parameters: { albumId: '7' },
|
|
159
|
+
headers: { Authorization: 'Bearer token' },
|
|
160
|
+
});
|
|
161
|
+
console.log('Upload started with ID:', id);
|
|
162
|
+
```
|
|
163
|
+
|
|
145
164
|
### Example with Capacitor Camera preview
|
|
146
165
|
|
|
147
166
|
Documentation for the [Capacitor Camera preview](https://github.com/Cap-go/camera-preview)
|
|
@@ -295,20 +314,30 @@ Get the native Capacitor plugin version.
|
|
|
295
314
|
|
|
296
315
|
#### uploadOption
|
|
297
316
|
|
|
317
|
+
| Prop | Type | Description | Default | Since |
|
|
318
|
+
| ----------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----- |
|
|
319
|
+
| **`filePath`** | <code>string</code> | The local file path of the file to upload. Can be a file:// URL or an absolute path. If you need to upload multiple files in a single multipart request, use `files`. | | 0.0.1 |
|
|
320
|
+
| **`files`** | <code>UploadFileOption[]</code> | Multiple files to upload in a single request. When provided, uploads are sent as `multipart/form-data` with one part per file. Use `fieldName` to control each part name (e.g. `images[]`). Note: `PUT` uploads (e.g. presigned S3 URLs) only support a single file. | | 0.0.3 |
|
|
321
|
+
| **`serverUrl`** | <code>string</code> | The server URL endpoint where the file should be uploaded. | | 0.0.1 |
|
|
322
|
+
| **`notificationTitle`** | <code>string</code> | The title of the upload notification shown to the user. Android only. | <code>'Uploading'</code> | 0.0.1 |
|
|
323
|
+
| **`headers`** | <code>{ [key: string]: string; }</code> | HTTP headers to send with the upload request. Useful for authentication tokens, content types, etc. | | 0.0.1 |
|
|
324
|
+
| **`method`** | <code>'PUT' \| 'POST'</code> | The HTTP method to use for the upload request. | <code>'POST'</code> | 0.0.1 |
|
|
325
|
+
| **`mimeType`** | <code>string</code> | The MIME type of the file being uploaded. If not specified, the plugin will attempt to determine it automatically. | | 0.0.1 |
|
|
326
|
+
| **`parameters`** | <code>{ [key: string]: string; }</code> | Additional form parameters to send with the upload request. These will be included as form data in multipart uploads. | | 0.0.1 |
|
|
327
|
+
| **`maxRetries`** | <code>number</code> | The maximum number of times to retry the upload if it fails. | <code>0</code> | 0.0.1 |
|
|
328
|
+
| **`uploadType`** | <code>'binary' \| 'multipart'</code> | The type of upload to perform. - 'binary': Uploads the file as raw binary data in the request body - 'multipart': Uploads the file as multipart/form-data | <code>'binary' when `method` is `'PUT'`, otherwise `'multipart'`</code> | 0.0.2 |
|
|
329
|
+
| **`fileField`** | <code>string</code> | The form field name for the file when using multipart upload type. Only used when uploadType is 'multipart'. For multi-file uploads via `files`, this is used as the default field name when a file entry does not specify `fieldName`. | <code>'file'</code> | 0.0.2 |
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
#### UploadFileOption
|
|
333
|
+
|
|
298
334
|
Configuration options for uploading a file.
|
|
299
335
|
|
|
300
|
-
| Prop
|
|
301
|
-
|
|
|
302
|
-
| **`filePath`**
|
|
303
|
-
| **`
|
|
304
|
-
| **`
|
|
305
|
-
| **`headers`** | <code>{ [key: string]: string; }</code> | HTTP headers to send with the upload request. Useful for authentication tokens, content types, etc. | | 0.0.1 |
|
|
306
|
-
| **`method`** | <code>'PUT' \| 'POST'</code> | The HTTP method to use for the upload request. | <code>'POST'</code> | 0.0.1 |
|
|
307
|
-
| **`mimeType`** | <code>string</code> | The MIME type of the file being uploaded. If not specified, the plugin will attempt to determine it automatically. | | 0.0.1 |
|
|
308
|
-
| **`parameters`** | <code>{ [key: string]: string; }</code> | Additional form parameters to send with the upload request. These will be included as form data in multipart uploads. | | 0.0.1 |
|
|
309
|
-
| **`maxRetries`** | <code>number</code> | The maximum number of times to retry the upload if it fails. | <code>0</code> | 0.0.1 |
|
|
310
|
-
| **`uploadType`** | <code>'binary' \| 'multipart'</code> | The type of upload to perform. - 'binary': Uploads the file as raw binary data in the request body - 'multipart': Uploads the file as multipart/form-data | <code>'binary'</code> | 0.0.2 |
|
|
311
|
-
| **`fileField`** | <code>string</code> | The form field name for the file when using multipart upload type. Only used when uploadType is 'multipart'. | <code>'file'</code> | 0.0.2 |
|
|
336
|
+
| Prop | Type | Description | Since |
|
|
337
|
+
| --------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
|
|
338
|
+
| **`filePath`** | <code>string</code> | The local file path of the file to upload. Can be a file:// URL or an absolute path. | 0.0.3 |
|
|
339
|
+
| **`fieldName`** | <code>string</code> | The form field name for the file part when using multipart upload. If omitted, <a href="#uploadoption">`uploadOption.fileField`</a> is used (defaults to `'file'`). | 0.0.3 |
|
|
340
|
+
| **`mimeType`** | <code>string</code> | The MIME type of this file. If not specified, the plugin will attempt to determine it automatically. | 0.0.3 |
|
|
312
341
|
|
|
313
342
|
|
|
314
343
|
#### PluginListenerHandle
|
|
@@ -334,4 +363,3 @@ Event emitted during the upload lifecycle.
|
|
|
334
363
|
|
|
335
364
|
For the inspiration and the code on ios: https://github.com/Vydia/react-native-background-upload/tree/master
|
|
336
365
|
For the API definition: https://www.npmjs.com/package/cordova-plugin-background-upload-put-s3
|
|
337
|
-
|
|
@@ -5,6 +5,7 @@ import android.content.Context;
|
|
|
5
5
|
import android.database.Cursor;
|
|
6
6
|
import android.net.Uri;
|
|
7
7
|
import android.provider.OpenableColumns;
|
|
8
|
+
import java.util.List;
|
|
8
9
|
import java.util.Map;
|
|
9
10
|
import net.gotev.uploadservice.UploadServiceConfig;
|
|
10
11
|
import net.gotev.uploadservice.data.UploadNotificationConfig;
|
|
@@ -16,6 +17,19 @@ public class Uploader {
|
|
|
16
17
|
|
|
17
18
|
private final Context context;
|
|
18
19
|
|
|
20
|
+
public static class UploadFile {
|
|
21
|
+
|
|
22
|
+
public final String filePath;
|
|
23
|
+
public final String fieldName;
|
|
24
|
+
public final String mimeType;
|
|
25
|
+
|
|
26
|
+
public UploadFile(String filePath, String fieldName, String mimeType) {
|
|
27
|
+
this.filePath = filePath;
|
|
28
|
+
this.fieldName = fieldName;
|
|
29
|
+
this.mimeType = mimeType;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
public Uploader(Context context) {
|
|
20
34
|
this.context = context;
|
|
21
35
|
initializeUploadService(context);
|
|
@@ -41,26 +55,33 @@ public class Uploader {
|
|
|
41
55
|
}
|
|
42
56
|
|
|
43
57
|
public String startUpload(
|
|
44
|
-
|
|
58
|
+
List<UploadFile> files,
|
|
45
59
|
String serverUrl,
|
|
46
60
|
Map<String, String> headers,
|
|
47
61
|
Map<String, String> parameters,
|
|
48
62
|
String httpMethod,
|
|
49
63
|
String notificationTitle,
|
|
50
64
|
int maxRetries,
|
|
51
|
-
String
|
|
52
|
-
String uploadType,
|
|
53
|
-
String fileField
|
|
65
|
+
String uploadType
|
|
54
66
|
) throws Exception {
|
|
55
67
|
UploadNotificationConfig notificationConfig = createNotificationConfig(notificationTitle);
|
|
56
68
|
|
|
57
69
|
if ("multipart".equals(uploadType)) {
|
|
70
|
+
if (files == null || files.isEmpty()) {
|
|
71
|
+
throw new IllegalArgumentException("Missing required parameter: files");
|
|
72
|
+
}
|
|
58
73
|
MultipartUploadRequest request = new MultipartUploadRequest(context, serverUrl)
|
|
59
74
|
.setMethod(httpMethod)
|
|
60
75
|
.setNotificationConfig((ctx, uploadId) -> notificationConfig)
|
|
61
76
|
.setMaxRetries(maxRetries);
|
|
62
77
|
|
|
63
|
-
|
|
78
|
+
for (UploadFile file : files) {
|
|
79
|
+
if (file == null || file.filePath == null || file.filePath.isEmpty()) {
|
|
80
|
+
throw new IllegalArgumentException("Invalid file entry in files");
|
|
81
|
+
}
|
|
82
|
+
String fieldName = (file.fieldName == null || file.fieldName.isEmpty()) ? "file" : file.fieldName;
|
|
83
|
+
request.addFileToUpload(file.filePath, fieldName, getFileNameFromUri(Uri.parse(file.filePath)), file.mimeType);
|
|
84
|
+
}
|
|
64
85
|
|
|
65
86
|
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
|
66
87
|
if (entry.getKey() != null && entry.getValue() != null) {
|
|
@@ -75,7 +96,23 @@ public class Uploader {
|
|
|
75
96
|
|
|
76
97
|
return request.startUpload();
|
|
77
98
|
} else {
|
|
78
|
-
|
|
99
|
+
if (files == null || files.isEmpty()) {
|
|
100
|
+
throw new IllegalArgumentException("Missing required parameter: filePath or files");
|
|
101
|
+
}
|
|
102
|
+
if (files.size() != 1) {
|
|
103
|
+
throw new IllegalArgumentException("Binary uploads only support a single file");
|
|
104
|
+
}
|
|
105
|
+
UploadFile file = files.get(0);
|
|
106
|
+
return startBinaryUpload(
|
|
107
|
+
file.filePath,
|
|
108
|
+
serverUrl,
|
|
109
|
+
headers,
|
|
110
|
+
parameters,
|
|
111
|
+
httpMethod,
|
|
112
|
+
notificationConfig,
|
|
113
|
+
maxRetries,
|
|
114
|
+
file.mimeType
|
|
115
|
+
);
|
|
79
116
|
}
|
|
80
117
|
}
|
|
81
118
|
|
|
@@ -6,11 +6,13 @@ import android.content.Context;
|
|
|
6
6
|
import android.net.Uri;
|
|
7
7
|
import android.os.Build;
|
|
8
8
|
import android.webkit.MimeTypeMap;
|
|
9
|
+
import com.getcapacitor.JSArray;
|
|
9
10
|
import com.getcapacitor.JSObject;
|
|
10
11
|
import com.getcapacitor.Plugin;
|
|
11
12
|
import com.getcapacitor.PluginCall;
|
|
12
13
|
import com.getcapacitor.PluginMethod;
|
|
13
14
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
15
|
+
import java.util.ArrayList;
|
|
14
16
|
import java.util.HashMap;
|
|
15
17
|
import java.util.Iterator;
|
|
16
18
|
import java.util.Map;
|
|
@@ -18,6 +20,7 @@ import net.gotev.uploadservice.data.UploadInfo;
|
|
|
18
20
|
import net.gotev.uploadservice.network.ServerResponse;
|
|
19
21
|
import net.gotev.uploadservice.observer.request.RequestObserver;
|
|
20
22
|
import net.gotev.uploadservice.observer.request.RequestObserverDelegate;
|
|
23
|
+
import org.json.JSONObject;
|
|
21
24
|
|
|
22
25
|
@CapacitorPlugin(name = "Uploader")
|
|
23
26
|
public class UploaderPlugin extends Plugin {
|
|
@@ -26,7 +29,7 @@ public class UploaderPlugin extends Plugin {
|
|
|
26
29
|
|
|
27
30
|
private static final String CAPACITOR_CONTENT_PATH_PREFIX = "/_capacitor_content_";
|
|
28
31
|
|
|
29
|
-
private final String pluginVersion = "8.
|
|
32
|
+
private final String pluginVersion = "8.2.0";
|
|
30
33
|
|
|
31
34
|
private Uploader implementation;
|
|
32
35
|
|
|
@@ -117,46 +120,76 @@ public class UploaderPlugin extends Plugin {
|
|
|
117
120
|
@PluginMethod
|
|
118
121
|
public void startUpload(PluginCall call) {
|
|
119
122
|
String filePath = call.getString("filePath");
|
|
123
|
+
JSArray filesArray = call.getArray("files");
|
|
120
124
|
String serverUrl = call.getString("serverUrl");
|
|
121
125
|
|
|
122
|
-
if (filePath == null || filePath.isEmpty()) {
|
|
123
|
-
call.reject("Missing required parameter: filePath");
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
126
|
if (serverUrl == null || serverUrl.isEmpty()) {
|
|
127
127
|
call.reject("Missing required parameter: serverUrl");
|
|
128
128
|
return;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
// Convert Capacitor web-accessible URLs to paths native code can open.
|
|
132
|
-
// Capacitor 8+ removed Bridge.getLocalUrl(String); mirror AndroidProtocolHandler logic.
|
|
133
|
-
String localFilePath = resolveCapacitorPath(filePath);
|
|
134
|
-
|
|
135
131
|
JSObject headersObj = call.getObject("headers", new JSObject());
|
|
136
132
|
JSObject parametersObj = call.getObject("parameters", new JSObject());
|
|
137
133
|
String httpMethod = call.getString("method", "POST");
|
|
138
134
|
String notificationTitle = call.getString("notificationTitle", "File Upload");
|
|
139
135
|
int maxRetries = call.getInt("maxRetries", 2);
|
|
140
|
-
String uploadType = call.getString("uploadType"
|
|
136
|
+
String uploadType = call.getString("uploadType");
|
|
137
|
+
if (uploadType == null || uploadType.isEmpty()) {
|
|
138
|
+
uploadType = "PUT".equalsIgnoreCase(httpMethod) ? "binary" : "multipart";
|
|
139
|
+
}
|
|
141
140
|
String fileField = call.getString("fileField", "file");
|
|
142
141
|
|
|
143
142
|
Map<String, String> headers = JSObjectToMap(headersObj);
|
|
144
143
|
Map<String, String> parameters = JSObjectToMap(parametersObj);
|
|
145
144
|
|
|
146
145
|
try {
|
|
147
|
-
|
|
146
|
+
ArrayList<Uploader.UploadFile> filesToUpload = new ArrayList<>();
|
|
147
|
+
|
|
148
|
+
if (filesArray != null && filesArray.length() > 0) {
|
|
149
|
+
for (int i = 0; i < filesArray.length(); i++) {
|
|
150
|
+
JSONObject fileObj = filesArray.getJSONObject(i);
|
|
151
|
+
String rawPath = fileObj.optString("filePath", null);
|
|
152
|
+
if (rawPath == null || rawPath.isEmpty()) {
|
|
153
|
+
call.reject("Missing required parameter: files[" + i + "].filePath");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Convert Capacitor web-accessible URLs to paths native code can open.
|
|
158
|
+
// Capacitor 8+ removed Bridge.getLocalUrl(String); mirror AndroidProtocolHandler logic.
|
|
159
|
+
String localPath = resolveCapacitorPath(rawPath);
|
|
160
|
+
String fieldName = fileObj.optString("fieldName", fileField);
|
|
161
|
+
|
|
162
|
+
String mimeType = null;
|
|
163
|
+
if (fileObj.has("mimeType")) {
|
|
164
|
+
mimeType = fileObj.optString("mimeType", null);
|
|
165
|
+
} else {
|
|
166
|
+
mimeType = call.getString("mimeType", null);
|
|
167
|
+
}
|
|
168
|
+
if (mimeType == null || mimeType.isEmpty()) {
|
|
169
|
+
mimeType = getMimeType(localPath);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
filesToUpload.add(new Uploader.UploadFile(localPath, fieldName, mimeType));
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
if (filePath == null || filePath.isEmpty()) {
|
|
176
|
+
call.reject("Missing required parameter: filePath or files");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
String localFilePath = resolveCapacitorPath(filePath);
|
|
180
|
+
String mimeType = call.getString("mimeType", getMimeType(localFilePath));
|
|
181
|
+
filesToUpload.add(new Uploader.UploadFile(localFilePath, fileField, mimeType));
|
|
182
|
+
}
|
|
148
183
|
|
|
149
184
|
String id = implementation.startUpload(
|
|
150
|
-
|
|
185
|
+
filesToUpload,
|
|
151
186
|
serverUrl,
|
|
152
187
|
headers,
|
|
153
188
|
parameters,
|
|
154
189
|
httpMethod,
|
|
155
190
|
notificationTitle,
|
|
156
191
|
maxRetries,
|
|
157
|
-
|
|
158
|
-
uploadType,
|
|
159
|
-
fileField
|
|
192
|
+
uploadType
|
|
160
193
|
);
|
|
161
194
|
JSObject result = new JSObject();
|
|
162
195
|
result.put("id", id);
|
package/dist/docs.json
CHANGED
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
{
|
|
42
42
|
"name": "example",
|
|
43
43
|
"text": "```typescript\nconst { id } = await Uploader.startUpload({\n filePath: 'file:///path/to/file.jpg',\n serverUrl: 'https://example.com/upload',\n headers: {\n 'Authorization': 'Bearer token'\n },\n method: 'POST',\n uploadType: 'multipart',\n fileField: 'photo'\n});\nconsole.log('Upload started with ID:', id);\n```"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "example",
|
|
47
|
+
"text": "```typescript\nconst { id } = await Uploader.startUpload({\n serverUrl: 'https://api.example.com/upload',\n method: 'POST',\n uploadType: 'multipart',\n files: [\n { filePath: 'file:///...photo1.jpg', fieldName: 'images[]', mimeType: 'image/jpeg' },\n { filePath: 'file:///...photo2.jpg', fieldName: 'images[]', mimeType: 'image/jpeg' },\n ],\n parameters: { albumId: '7' },\n headers: { Authorization: 'Bearer token' },\n});\nconsole.log('Upload started with ID:', id);\n```"
|
|
44
48
|
}
|
|
45
49
|
],
|
|
46
50
|
"docs": "Start uploading a file to a server.\n\nThe upload will continue in the background even if the app is closed or backgrounded.\nListen to upload events to track progress, completion, or failure.",
|
|
@@ -165,13 +169,8 @@
|
|
|
165
169
|
{
|
|
166
170
|
"name": "uploadOption",
|
|
167
171
|
"slug": "uploadoption",
|
|
168
|
-
"docs": "
|
|
169
|
-
"tags": [
|
|
170
|
-
{
|
|
171
|
-
"text": "0.0.1",
|
|
172
|
-
"name": "since"
|
|
173
|
-
}
|
|
174
|
-
],
|
|
172
|
+
"docs": "",
|
|
173
|
+
"tags": [],
|
|
175
174
|
"methods": [],
|
|
176
175
|
"properties": [
|
|
177
176
|
{
|
|
@@ -182,9 +181,23 @@
|
|
|
182
181
|
"name": "since"
|
|
183
182
|
}
|
|
184
183
|
],
|
|
185
|
-
"docs": "The local file path of the file to upload.\nCan be a file:// URL or an absolute path
|
|
184
|
+
"docs": "The local file path of the file to upload.\nCan be a file:// URL or an absolute path.\n\nIf you need to upload multiple files in a single multipart request, use `files`.",
|
|
186
185
|
"complexTypes": [],
|
|
187
|
-
"type": "string"
|
|
186
|
+
"type": "string | undefined"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"name": "files",
|
|
190
|
+
"tags": [
|
|
191
|
+
{
|
|
192
|
+
"text": "0.0.3",
|
|
193
|
+
"name": "since"
|
|
194
|
+
}
|
|
195
|
+
],
|
|
196
|
+
"docs": "Multiple files to upload in a single request.\n\nWhen provided, uploads are sent as `multipart/form-data` with one part per file.\nUse `fieldName` to control each part name (e.g. `images[]`).\n\nNote: `PUT` uploads (e.g. presigned S3 URLs) only support a single file.",
|
|
197
|
+
"complexTypes": [
|
|
198
|
+
"UploadFileOption"
|
|
199
|
+
],
|
|
200
|
+
"type": "UploadFileOption[] | undefined"
|
|
188
201
|
},
|
|
189
202
|
{
|
|
190
203
|
"name": "serverUrl",
|
|
@@ -294,7 +307,7 @@
|
|
|
294
307
|
"name": "uploadType",
|
|
295
308
|
"tags": [
|
|
296
309
|
{
|
|
297
|
-
"text": "'binary'",
|
|
310
|
+
"text": "'binary' when `method` is `'PUT'`, otherwise `'multipart'`",
|
|
298
311
|
"name": "default"
|
|
299
312
|
},
|
|
300
313
|
{
|
|
@@ -318,7 +331,57 @@
|
|
|
318
331
|
"name": "since"
|
|
319
332
|
}
|
|
320
333
|
],
|
|
321
|
-
"docs": "The form field name for the file when using multipart upload type.\nOnly used when uploadType is 'multipart'
|
|
334
|
+
"docs": "The form field name for the file when using multipart upload type.\nOnly used when uploadType is 'multipart'.\n\nFor multi-file uploads via `files`, this is used as the default field name\nwhen a file entry does not specify `fieldName`.",
|
|
335
|
+
"complexTypes": [],
|
|
336
|
+
"type": "string | undefined"
|
|
337
|
+
}
|
|
338
|
+
]
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"name": "UploadFileOption",
|
|
342
|
+
"slug": "uploadfileoption",
|
|
343
|
+
"docs": "Configuration options for uploading a file.",
|
|
344
|
+
"tags": [
|
|
345
|
+
{
|
|
346
|
+
"text": "0.0.1",
|
|
347
|
+
"name": "since"
|
|
348
|
+
}
|
|
349
|
+
],
|
|
350
|
+
"methods": [],
|
|
351
|
+
"properties": [
|
|
352
|
+
{
|
|
353
|
+
"name": "filePath",
|
|
354
|
+
"tags": [
|
|
355
|
+
{
|
|
356
|
+
"text": "0.0.3",
|
|
357
|
+
"name": "since"
|
|
358
|
+
}
|
|
359
|
+
],
|
|
360
|
+
"docs": "The local file path of the file to upload.\nCan be a file:// URL or an absolute path.",
|
|
361
|
+
"complexTypes": [],
|
|
362
|
+
"type": "string"
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
"name": "fieldName",
|
|
366
|
+
"tags": [
|
|
367
|
+
{
|
|
368
|
+
"text": "0.0.3",
|
|
369
|
+
"name": "since"
|
|
370
|
+
}
|
|
371
|
+
],
|
|
372
|
+
"docs": "The form field name for the file part when using multipart upload.\n\nIf omitted, `uploadOption.fileField` is used (defaults to `'file'`).",
|
|
373
|
+
"complexTypes": [],
|
|
374
|
+
"type": "string | undefined"
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
"name": "mimeType",
|
|
378
|
+
"tags": [
|
|
379
|
+
{
|
|
380
|
+
"text": "0.0.3",
|
|
381
|
+
"name": "since"
|
|
382
|
+
}
|
|
383
|
+
],
|
|
384
|
+
"docs": "The MIME type of this file.\nIf not specified, the plugin will attempt to determine it automatically.",
|
|
322
385
|
"complexTypes": [],
|
|
323
386
|
"type": "string | undefined"
|
|
324
387
|
}
|
|
@@ -4,14 +4,51 @@ import type { PluginListenerHandle } from '@capacitor/core';
|
|
|
4
4
|
*
|
|
5
5
|
* @since 0.0.1
|
|
6
6
|
*/
|
|
7
|
+
export interface UploadFileOption {
|
|
8
|
+
/**
|
|
9
|
+
* The local file path of the file to upload.
|
|
10
|
+
* Can be a file:// URL or an absolute path.
|
|
11
|
+
*
|
|
12
|
+
* @since 0.0.3
|
|
13
|
+
*/
|
|
14
|
+
filePath: string;
|
|
15
|
+
/**
|
|
16
|
+
* The form field name for the file part when using multipart upload.
|
|
17
|
+
*
|
|
18
|
+
* If omitted, `uploadOption.fileField` is used (defaults to `'file'`).
|
|
19
|
+
*
|
|
20
|
+
* @since 0.0.3
|
|
21
|
+
*/
|
|
22
|
+
fieldName?: string;
|
|
23
|
+
/**
|
|
24
|
+
* The MIME type of this file.
|
|
25
|
+
* If not specified, the plugin will attempt to determine it automatically.
|
|
26
|
+
*
|
|
27
|
+
* @since 0.0.3
|
|
28
|
+
*/
|
|
29
|
+
mimeType?: string;
|
|
30
|
+
}
|
|
7
31
|
export interface uploadOption {
|
|
8
32
|
/**
|
|
9
33
|
* The local file path of the file to upload.
|
|
10
34
|
* Can be a file:// URL or an absolute path.
|
|
11
35
|
*
|
|
36
|
+
* If you need to upload multiple files in a single multipart request, use `files`.
|
|
37
|
+
*
|
|
12
38
|
* @since 0.0.1
|
|
13
39
|
*/
|
|
14
|
-
filePath
|
|
40
|
+
filePath?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Multiple files to upload in a single request.
|
|
43
|
+
*
|
|
44
|
+
* When provided, uploads are sent as `multipart/form-data` with one part per file.
|
|
45
|
+
* Use `fieldName` to control each part name (e.g. `images[]`).
|
|
46
|
+
*
|
|
47
|
+
* Note: `PUT` uploads (e.g. presigned S3 URLs) only support a single file.
|
|
48
|
+
*
|
|
49
|
+
* @since 0.0.3
|
|
50
|
+
*/
|
|
51
|
+
files?: UploadFileOption[];
|
|
15
52
|
/**
|
|
16
53
|
* The server URL endpoint where the file should be uploaded.
|
|
17
54
|
*
|
|
@@ -78,7 +115,7 @@ export interface uploadOption {
|
|
|
78
115
|
* - 'binary': Uploads the file as raw binary data in the request body
|
|
79
116
|
* - 'multipart': Uploads the file as multipart/form-data
|
|
80
117
|
*
|
|
81
|
-
* @default 'binary'
|
|
118
|
+
* @default 'binary' when `method` is `'PUT'`, otherwise `'multipart'`
|
|
82
119
|
* @since 0.0.2
|
|
83
120
|
*/
|
|
84
121
|
uploadType?: 'binary' | 'multipart';
|
|
@@ -86,6 +123,9 @@ export interface uploadOption {
|
|
|
86
123
|
* The form field name for the file when using multipart upload type.
|
|
87
124
|
* Only used when uploadType is 'multipart'.
|
|
88
125
|
*
|
|
126
|
+
* For multi-file uploads via `files`, this is used as the default field name
|
|
127
|
+
* when a file entry does not specify `fieldName`.
|
|
128
|
+
*
|
|
89
129
|
* @default 'file'
|
|
90
130
|
* @since 0.0.2
|
|
91
131
|
*/
|
|
@@ -171,6 +211,22 @@ export interface UploaderPlugin {
|
|
|
171
211
|
* });
|
|
172
212
|
* console.log('Upload started with ID:', id);
|
|
173
213
|
* ```
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```typescript
|
|
217
|
+
* const { id } = await Uploader.startUpload({
|
|
218
|
+
* serverUrl: 'https://api.example.com/upload',
|
|
219
|
+
* method: 'POST',
|
|
220
|
+
* uploadType: 'multipart',
|
|
221
|
+
* files: [
|
|
222
|
+
* { filePath: 'file:///...photo1.jpg', fieldName: 'images[]', mimeType: 'image/jpeg' },
|
|
223
|
+
* { filePath: 'file:///...photo2.jpg', fieldName: 'images[]', mimeType: 'image/jpeg' },
|
|
224
|
+
* ],
|
|
225
|
+
* parameters: { albumId: '7' },
|
|
226
|
+
* headers: { Authorization: 'Bearer token' },
|
|
227
|
+
* });
|
|
228
|
+
* console.log('Upload started with ID:', id);
|
|
229
|
+
* ```
|
|
174
230
|
*/
|
|
175
231
|
startUpload(options: uploadOption): Promise<{
|
|
176
232
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * Configuration options for uploading a file.\n *\n * @since 0.0.1\n */\nexport interface uploadOption {\n /**\n * The local file path of the file to upload.\n * Can be a file:// URL or an absolute path.\n *\n * @since 0.0.1\n */\n filePath
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * Configuration options for uploading a file.\n *\n * @since 0.0.1\n */\nexport interface UploadFileOption {\n /**\n * The local file path of the file to upload.\n * Can be a file:// URL or an absolute path.\n *\n * @since 0.0.3\n */\n filePath: string;\n\n /**\n * The form field name for the file part when using multipart upload.\n *\n * If omitted, `uploadOption.fileField` is used (defaults to `'file'`).\n *\n * @since 0.0.3\n */\n fieldName?: string;\n\n /**\n * The MIME type of this file.\n * If not specified, the plugin will attempt to determine it automatically.\n *\n * @since 0.0.3\n */\n mimeType?: string;\n}\n\nexport interface uploadOption {\n /**\n * The local file path of the file to upload.\n * Can be a file:// URL or an absolute path.\n *\n * If you need to upload multiple files in a single multipart request, use `files`.\n *\n * @since 0.0.1\n */\n filePath?: string;\n\n /**\n * Multiple files to upload in a single request.\n *\n * When provided, uploads are sent as `multipart/form-data` with one part per file.\n * Use `fieldName` to control each part name (e.g. `images[]`).\n *\n * Note: `PUT` uploads (e.g. presigned S3 URLs) only support a single file.\n *\n * @since 0.0.3\n */\n files?: UploadFileOption[];\n\n /**\n * The server URL endpoint where the file should be uploaded.\n *\n * @since 0.0.1\n */\n serverUrl: string;\n\n /**\n * The title of the upload notification shown to the user.\n * Android only.\n *\n * @default 'Uploading'\n * @since 0.0.1\n */\n notificationTitle?: string;\n\n /**\n * HTTP headers to send with the upload request.\n * Useful for authentication tokens, content types, etc.\n *\n * @since 0.0.1\n * @example\n * ```typescript\n * headers: {\n * 'Authorization': 'Bearer token123',\n * 'X-Custom-Header': 'value'\n * }\n * ```\n */\n headers: {\n [key: string]: string;\n };\n\n /**\n * The HTTP method to use for the upload request.\n *\n * @default 'POST'\n * @since 0.0.1\n */\n method?: 'PUT' | 'POST';\n\n /**\n * The MIME type of the file being uploaded.\n * If not specified, the plugin will attempt to determine it automatically.\n *\n * @since 0.0.1\n * @example 'image/jpeg', 'application/pdf', 'video/mp4'\n */\n mimeType?: string;\n\n /**\n * Additional form parameters to send with the upload request.\n * These will be included as form data in multipart uploads.\n *\n * @since 0.0.1\n */\n parameters?: { [key: string]: string };\n\n /**\n * The maximum number of times to retry the upload if it fails.\n *\n * @since 0.0.1\n * @default 0\n */\n maxRetries?: number;\n\n /**\n * The type of upload to perform.\n * - 'binary': Uploads the file as raw binary data in the request body\n * - 'multipart': Uploads the file as multipart/form-data\n *\n * @default 'binary' when `method` is `'PUT'`, otherwise `'multipart'`\n * @since 0.0.2\n */\n uploadType?: 'binary' | 'multipart';\n\n /**\n * The form field name for the file when using multipart upload type.\n * Only used when uploadType is 'multipart'.\n *\n * For multi-file uploads via `files`, this is used as the default field name\n * when a file entry does not specify `fieldName`.\n *\n * @default 'file'\n * @since 0.0.2\n */\n fileField?: string;\n}\n\n/**\n * Event emitted during the upload lifecycle.\n *\n * @since 0.0.1\n */\nexport interface UploadEvent {\n /**\n * The current status of the upload.\n * - 'uploading': Upload is in progress\n * - 'completed': Upload finished successfully\n * - 'failed': Upload encountered an error\n *\n * @since 0.0.1\n */\n name: 'uploading' | 'completed' | 'failed';\n\n /**\n * Additional data about the upload event.\n *\n * @since 0.0.1\n */\n payload: {\n /**\n * Upload progress percentage from 0 to 100.\n * Only present during 'uploading' events.\n *\n * @since 0.0.1\n */\n percent?: number;\n\n /**\n * Error message if the upload failed.\n * Only present during 'failed' events.\n *\n * @since 0.0.1\n */\n error?: string;\n\n /**\n * HTTP status code returned by the server.\n * Present during 'completed' and 'failed' events.\n *\n * @since 0.0.1\n */\n statusCode?: number;\n };\n\n /**\n * Unique identifier for this upload task.\n *\n * @since 0.0.1\n */\n id: string;\n}\n\n/**\n * Capacitor Uploader Plugin for uploading files with background support and progress tracking.\n *\n * @since 0.0.1\n */\nexport interface UploaderPlugin {\n /**\n * Start uploading a file to a server.\n *\n * The upload will continue in the background even if the app is closed or backgrounded.\n * Listen to upload events to track progress, completion, or failure.\n *\n * @param options - Configuration for the upload\n * @returns Promise that resolves with the upload ID\n * @throws Error if the upload fails to start\n * @since 0.0.1\n * @example\n * ```typescript\n * const { id } = await Uploader.startUpload({\n * filePath: 'file:///path/to/file.jpg',\n * serverUrl: 'https://example.com/upload',\n * headers: {\n * 'Authorization': 'Bearer token'\n * },\n * method: 'POST',\n * uploadType: 'multipart',\n * fileField: 'photo'\n * });\n * console.log('Upload started with ID:', id);\n * ```\n *\n * @example\n * ```typescript\n * const { id } = await Uploader.startUpload({\n * serverUrl: 'https://api.example.com/upload',\n * method: 'POST',\n * uploadType: 'multipart',\n * files: [\n * { filePath: 'file:///...photo1.jpg', fieldName: 'images[]', mimeType: 'image/jpeg' },\n * { filePath: 'file:///...photo2.jpg', fieldName: 'images[]', mimeType: 'image/jpeg' },\n * ],\n * parameters: { albumId: '7' },\n * headers: { Authorization: 'Bearer token' },\n * });\n * console.log('Upload started with ID:', id);\n * ```\n */\n startUpload(options: uploadOption): Promise<{ id: string }>;\n\n /**\n * Cancel and remove an ongoing upload.\n *\n * This will stop the upload if it's in progress and clean up resources.\n *\n * @param options - Object containing the upload ID to remove\n * @returns Promise that resolves when the upload is removed\n * @throws Error if the upload ID is not found\n * @since 0.0.1\n * @example\n * ```typescript\n * await Uploader.removeUpload({ id: 'upload-123' });\n * ```\n */\n removeUpload(options: { id: string }): Promise<void>;\n\n /**\n * Listen for upload progress and status events.\n *\n * Events are fired for:\n * - Upload progress updates (with percent)\n * - Upload completion (with statusCode)\n * - Upload failure (with error and statusCode)\n *\n * @param eventName - Must be 'events'\n * @param listenerFunc - Callback function to handle upload events\n * @returns Promise that resolves with a listener handle for removal\n * @since 0.0.1\n * @example\n * ```typescript\n * const listener = await Uploader.addListener('events', (event) => {\n * if (event.name === 'uploading') {\n * console.log(`Upload ${event.id}: ${event.payload.percent}%`);\n * } else if (event.name === 'completed') {\n * console.log(`Upload ${event.id} completed`);\n * } else if (event.name === 'failed') {\n * console.error(`Upload ${event.id} failed:`, event.payload.error);\n * }\n * });\n *\n * // Remove listener when done\n * await listener.remove();\n * ```\n */\n addListener(eventName: 'events', listenerFunc: (state: UploadEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Get the native Capacitor plugin version.\n *\n * @returns Promise that resolves with the plugin version\n * @throws Error if getting the version fails\n * @since 0.0.1\n * @example\n * ```typescript\n * const { version } = await Uploader.getPluginVersion();\n * console.log('Plugin version:', version);\n * ```\n */\n getPluginVersion(): Promise<{ version: string }>;\n}\n"]}
|
package/dist/esm/web.d.ts
CHANGED
package/dist/esm/web.js
CHANGED
|
@@ -29,23 +29,44 @@ export class UploaderWeb extends WebPlugin {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
async doUpload(id, options) {
|
|
32
|
-
|
|
32
|
+
var _a, _b, _c;
|
|
33
|
+
const { serverUrl, headers = {}, method = 'POST', parameters = {} } = options;
|
|
33
34
|
const upload = this.uploads.get(id);
|
|
34
35
|
if (!upload)
|
|
35
36
|
return;
|
|
36
37
|
try {
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
throw new Error('
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
const files = this.normalizeFiles(options);
|
|
39
|
+
if (files.length === 0)
|
|
40
|
+
throw new Error('Missing required parameter: filePath or files');
|
|
41
|
+
const resolvedMethod = method.toUpperCase();
|
|
42
|
+
const uploadType = (_a = options.uploadType) !== null && _a !== void 0 ? _a : (resolvedMethod === 'PUT' ? 'binary' : 'multipart');
|
|
43
|
+
let body;
|
|
44
|
+
if (resolvedMethod === 'PUT' || uploadType === 'binary') {
|
|
45
|
+
if (files.length !== 1)
|
|
46
|
+
throw new Error('Binary uploads only support a single file');
|
|
47
|
+
const file = await this.getFileFromPath(files[0].filePath);
|
|
48
|
+
if (!file)
|
|
49
|
+
throw new Error('File not found');
|
|
50
|
+
body = file;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const formData = new FormData();
|
|
54
|
+
for (const fileOption of files) {
|
|
55
|
+
const file = await this.getFileFromPath(fileOption.filePath);
|
|
56
|
+
if (!file)
|
|
57
|
+
throw new Error('File not found');
|
|
58
|
+
const fieldName = (_c = (_b = fileOption.fieldName) !== null && _b !== void 0 ? _b : options.fileField) !== null && _c !== void 0 ? _c : 'file';
|
|
59
|
+
formData.append(fieldName, file);
|
|
60
|
+
}
|
|
61
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
62
|
+
formData.append(key, value);
|
|
63
|
+
}
|
|
64
|
+
body = formData;
|
|
44
65
|
}
|
|
45
66
|
const response = await fetch(serverUrl, {
|
|
46
|
-
method,
|
|
67
|
+
method: resolvedMethod,
|
|
47
68
|
headers,
|
|
48
|
-
body
|
|
69
|
+
body,
|
|
49
70
|
signal: upload.controller.signal,
|
|
50
71
|
});
|
|
51
72
|
if (!response.ok)
|
|
@@ -75,6 +96,20 @@ export class UploaderWeb extends WebPlugin {
|
|
|
75
96
|
}
|
|
76
97
|
}
|
|
77
98
|
}
|
|
99
|
+
normalizeFiles(options) {
|
|
100
|
+
if (options.files && options.files.length > 0)
|
|
101
|
+
return options.files;
|
|
102
|
+
if (options.filePath) {
|
|
103
|
+
return [
|
|
104
|
+
{
|
|
105
|
+
filePath: options.filePath,
|
|
106
|
+
fieldName: options.fileField,
|
|
107
|
+
mimeType: options.mimeType,
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
}
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
78
113
|
async getFileFromPath(filePath) {
|
|
79
114
|
// Check if the path is an IndexedDB path
|
|
80
115
|
if (PathHelper.isIndexedDBPath(filePath)) {
|
package/dist/esm/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport { openDB } from 'idb';\n\nimport { PathHelper } from './PathHelper';\nimport type { UploaderPlugin, uploadOption } from './definitions';\n\nexport class UploaderWeb extends WebPlugin implements UploaderPlugin {\n private uploads: Map<string, { controller: AbortController; retries: number }> = new Map();\n\n async startUpload(options: uploadOption): Promise<{ id: string }> {\n console.log('startUpload', options);\n\n const id = Math.random().toString(36).substring(2, 15);\n const controller = new AbortController();\n const maxRetries = options.maxRetries || 3;\n this.uploads.set(id, { controller, retries: maxRetries });\n\n this.doUpload(id, options);\n\n return { id };\n }\n\n async removeUpload(options: { id: string }): Promise<void> {\n console.log('removeUpload', options);\n const upload = this.uploads.get(options.id);\n if (upload) {\n upload.controller.abort();\n this.uploads.delete(options.id);\n this.notifyListeners('events', {\n name: 'cancelled',\n id: options.id,\n payload: {},\n });\n }\n }\n\n private async doUpload(id: string, options: uploadOption) {\n const { filePath, serverUrl, headers = {}, method = 'POST', parameters = {} } = options;\n const upload = this.uploads.get(id);\n\n if (!upload) return;\n\n try {\n const file = await this.getFileFromPath(filePath);\n if (!file) throw new Error('File not found');\n\n const formData = new FormData();\n formData.append('file', file);\n\n for (const [key, value] of Object.entries(parameters)) {\n formData.append(key, value);\n }\n\n const response = await fetch(serverUrl, {\n method,\n headers,\n body: method === 'PUT' ? file : formData,\n signal: upload.controller.signal,\n });\n\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);\n\n this.notifyListeners('events', {\n name: 'completed',\n id,\n payload: { statusCode: response.status },\n });\n\n this.uploads.delete(id);\n } catch (error) {\n if ((error as Error).name === 'AbortError') return;\n\n if (upload.retries > 0) {\n upload.retries--;\n console.log(`Retrying upload (retries left: ${upload.retries})`);\n setTimeout(() => this.doUpload(id, options), 1000);\n } else {\n this.notifyListeners('events', {\n name: 'failed',\n id,\n payload: { error: (error as Error).message },\n });\n this.uploads.delete(id);\n }\n }\n }\n\n private async getFileFromPath(filePath: string): Promise<File | null> {\n // Check if the path is an IndexedDB path\n if (PathHelper.isIndexedDBPath(filePath)) {\n return this.getFileFromIndexedDB(filePath);\n }\n\n // Otherwise, treat it as a file path from the system\n return this.getFileFromSystem(filePath);\n }\n\n // Retrieve the file from IndexedDB\n private async getFileFromIndexedDB(filePath: string): Promise<File | null> {\n // Parse the path to get the database, store name, and key\n const { database, storeName, key } = PathHelper.parseIndexedDBPath(filePath);\n\n try {\n // Open the IndexedDB database and access the object store\n const db = await openDB(database, 1, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n },\n });\n\n // Get the blob from the store\n const blob = await db.get(storeName, key);\n if (!blob) {\n console.error(`File with key \"${key}\" not found in store \"${storeName}\" in database \"${database}\"`);\n return null;\n }\n\n // Convert the Blob to a File object\n return new File([blob], key, { type: blob.type });\n } catch (error) {\n console.error('Error retrieving file from IndexedDB:', error);\n return null;\n }\n }\n\n // Retrieve the file from the system (local file)\n private async getFileFromSystem(filePath: string): Promise<File | null> {\n try {\n // This is a simplified version. In a real-world scenario,\n // you might need to handle different types of paths or use a file system API.\n const response = await fetch(filePath);\n const blob = await response.blob();\n return new File([blob], filePath.split('/').pop() || 'file', {\n type: blob.type,\n });\n } catch (error) {\n console.error('Error getting file from system:', error);\n return null;\n }\n }\n\n async getPluginVersion(): Promise<{ version: string }> {\n return { version: 'web' };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport { openDB } from 'idb';\n\nimport { PathHelper } from './PathHelper';\nimport type { UploadFileOption, UploaderPlugin, uploadOption } from './definitions';\n\nexport class UploaderWeb extends WebPlugin implements UploaderPlugin {\n private uploads: Map<string, { controller: AbortController; retries: number }> = new Map();\n\n async startUpload(options: uploadOption): Promise<{ id: string }> {\n console.log('startUpload', options);\n\n const id = Math.random().toString(36).substring(2, 15);\n const controller = new AbortController();\n const maxRetries = options.maxRetries || 3;\n this.uploads.set(id, { controller, retries: maxRetries });\n\n this.doUpload(id, options);\n\n return { id };\n }\n\n async removeUpload(options: { id: string }): Promise<void> {\n console.log('removeUpload', options);\n const upload = this.uploads.get(options.id);\n if (upload) {\n upload.controller.abort();\n this.uploads.delete(options.id);\n this.notifyListeners('events', {\n name: 'cancelled',\n id: options.id,\n payload: {},\n });\n }\n }\n\n private async doUpload(id: string, options: uploadOption) {\n const { serverUrl, headers = {}, method = 'POST', parameters = {} } = options;\n const upload = this.uploads.get(id);\n\n if (!upload) return;\n\n try {\n const files = this.normalizeFiles(options);\n if (files.length === 0) throw new Error('Missing required parameter: filePath or files');\n\n const resolvedMethod = method.toUpperCase();\n const uploadType = options.uploadType ?? (resolvedMethod === 'PUT' ? 'binary' : 'multipart');\n\n let body: BodyInit;\n if (resolvedMethod === 'PUT' || uploadType === 'binary') {\n if (files.length !== 1) throw new Error('Binary uploads only support a single file');\n const file = await this.getFileFromPath(files[0].filePath);\n if (!file) throw new Error('File not found');\n body = file;\n } else {\n const formData = new FormData();\n\n for (const fileOption of files) {\n const file = await this.getFileFromPath(fileOption.filePath);\n if (!file) throw new Error('File not found');\n const fieldName = fileOption.fieldName ?? options.fileField ?? 'file';\n formData.append(fieldName, file);\n }\n\n for (const [key, value] of Object.entries(parameters)) {\n formData.append(key, value);\n }\n\n body = formData;\n }\n\n const response = await fetch(serverUrl, {\n method: resolvedMethod,\n headers,\n body,\n signal: upload.controller.signal,\n });\n\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);\n\n this.notifyListeners('events', {\n name: 'completed',\n id,\n payload: { statusCode: response.status },\n });\n\n this.uploads.delete(id);\n } catch (error) {\n if ((error as Error).name === 'AbortError') return;\n\n if (upload.retries > 0) {\n upload.retries--;\n console.log(`Retrying upload (retries left: ${upload.retries})`);\n setTimeout(() => this.doUpload(id, options), 1000);\n } else {\n this.notifyListeners('events', {\n name: 'failed',\n id,\n payload: { error: (error as Error).message },\n });\n this.uploads.delete(id);\n }\n }\n }\n\n private normalizeFiles(options: uploadOption): UploadFileOption[] {\n if (options.files && options.files.length > 0) return options.files;\n if (options.filePath) {\n return [\n {\n filePath: options.filePath,\n fieldName: options.fileField,\n mimeType: options.mimeType,\n },\n ];\n }\n return [];\n }\n\n private async getFileFromPath(filePath: string): Promise<File | null> {\n // Check if the path is an IndexedDB path\n if (PathHelper.isIndexedDBPath(filePath)) {\n return this.getFileFromIndexedDB(filePath);\n }\n\n // Otherwise, treat it as a file path from the system\n return this.getFileFromSystem(filePath);\n }\n\n // Retrieve the file from IndexedDB\n private async getFileFromIndexedDB(filePath: string): Promise<File | null> {\n // Parse the path to get the database, store name, and key\n const { database, storeName, key } = PathHelper.parseIndexedDBPath(filePath);\n\n try {\n // Open the IndexedDB database and access the object store\n const db = await openDB(database, 1, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n },\n });\n\n // Get the blob from the store\n const blob = await db.get(storeName, key);\n if (!blob) {\n console.error(`File with key \"${key}\" not found in store \"${storeName}\" in database \"${database}\"`);\n return null;\n }\n\n // Convert the Blob to a File object\n return new File([blob], key, { type: blob.type });\n } catch (error) {\n console.error('Error retrieving file from IndexedDB:', error);\n return null;\n }\n }\n\n // Retrieve the file from the system (local file)\n private async getFileFromSystem(filePath: string): Promise<File | null> {\n try {\n // This is a simplified version. In a real-world scenario,\n // you might need to handle different types of paths or use a file system API.\n const response = await fetch(filePath);\n const blob = await response.blob();\n return new File([blob], filePath.split('/').pop() || 'file', {\n type: blob.type,\n });\n } catch (error) {\n console.error('Error getting file from system:', error);\n return null;\n }\n }\n\n async getPluginVersion(): Promise<{ version: string }> {\n return { version: 'web' };\n }\n}\n"]}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -56,23 +56,44 @@ class UploaderWeb extends core.WebPlugin {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
async doUpload(id, options) {
|
|
59
|
-
|
|
59
|
+
var _a, _b, _c;
|
|
60
|
+
const { serverUrl, headers = {}, method = 'POST', parameters = {} } = options;
|
|
60
61
|
const upload = this.uploads.get(id);
|
|
61
62
|
if (!upload)
|
|
62
63
|
return;
|
|
63
64
|
try {
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
66
|
-
throw new Error('
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
const files = this.normalizeFiles(options);
|
|
66
|
+
if (files.length === 0)
|
|
67
|
+
throw new Error('Missing required parameter: filePath or files');
|
|
68
|
+
const resolvedMethod = method.toUpperCase();
|
|
69
|
+
const uploadType = (_a = options.uploadType) !== null && _a !== void 0 ? _a : (resolvedMethod === 'PUT' ? 'binary' : 'multipart');
|
|
70
|
+
let body;
|
|
71
|
+
if (resolvedMethod === 'PUT' || uploadType === 'binary') {
|
|
72
|
+
if (files.length !== 1)
|
|
73
|
+
throw new Error('Binary uploads only support a single file');
|
|
74
|
+
const file = await this.getFileFromPath(files[0].filePath);
|
|
75
|
+
if (!file)
|
|
76
|
+
throw new Error('File not found');
|
|
77
|
+
body = file;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const formData = new FormData();
|
|
81
|
+
for (const fileOption of files) {
|
|
82
|
+
const file = await this.getFileFromPath(fileOption.filePath);
|
|
83
|
+
if (!file)
|
|
84
|
+
throw new Error('File not found');
|
|
85
|
+
const fieldName = (_c = (_b = fileOption.fieldName) !== null && _b !== void 0 ? _b : options.fileField) !== null && _c !== void 0 ? _c : 'file';
|
|
86
|
+
formData.append(fieldName, file);
|
|
87
|
+
}
|
|
88
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
89
|
+
formData.append(key, value);
|
|
90
|
+
}
|
|
91
|
+
body = formData;
|
|
71
92
|
}
|
|
72
93
|
const response = await fetch(serverUrl, {
|
|
73
|
-
method,
|
|
94
|
+
method: resolvedMethod,
|
|
74
95
|
headers,
|
|
75
|
-
body
|
|
96
|
+
body,
|
|
76
97
|
signal: upload.controller.signal,
|
|
77
98
|
});
|
|
78
99
|
if (!response.ok)
|
|
@@ -102,6 +123,20 @@ class UploaderWeb extends core.WebPlugin {
|
|
|
102
123
|
}
|
|
103
124
|
}
|
|
104
125
|
}
|
|
126
|
+
normalizeFiles(options) {
|
|
127
|
+
if (options.files && options.files.length > 0)
|
|
128
|
+
return options.files;
|
|
129
|
+
if (options.filePath) {
|
|
130
|
+
return [
|
|
131
|
+
{
|
|
132
|
+
filePath: options.filePath,
|
|
133
|
+
fieldName: options.fileField,
|
|
134
|
+
mimeType: options.mimeType,
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
105
140
|
async getFileFromPath(filePath) {
|
|
106
141
|
// Check if the path is an IndexedDB path
|
|
107
142
|
if (PathHelper.isIndexedDBPath(filePath)) {
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/PathHelper.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst Uploader = registerPlugin('Uploader', {\n web: () => import('./web').then((m) => new m.UploaderWeb()),\n});\nexport * from './definitions';\nexport { Uploader };\n//# sourceMappingURL=index.js.map","export class PathHelper {\n // Check if the path follows the idb://[databaseName]/[storeName]/[key] format\n static isIndexedDBPath(path) {\n const regex = /^idb:\\/\\/([^/]+)\\/([^/]+)\\/(.+)$/;\n return regex.test(path);\n }\n // Parse the path to extract database, storeName, and key\n static parseIndexedDBPath(path) {\n const regex = /^idb:\\/\\/([^/]+)\\/([^/]+)\\/(.+)$/;\n const match = path.match(regex);\n if (!match) {\n throw new Error('Invalid IndexedDB path format');\n }\n return {\n database: match[1],\n storeName: match[2],\n key: match[3],\n };\n }\n}\n//# sourceMappingURL=PathHelper.js.map","import { WebPlugin } from '@capacitor/core';\nimport { openDB } from 'idb';\nimport { PathHelper } from './PathHelper';\nexport class UploaderWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.uploads = new Map();\n }\n async startUpload(options) {\n console.log('startUpload', options);\n const id = Math.random().toString(36).substring(2, 15);\n const controller = new AbortController();\n const maxRetries = options.maxRetries || 3;\n this.uploads.set(id, { controller, retries: maxRetries });\n this.doUpload(id, options);\n return { id };\n }\n async removeUpload(options) {\n console.log('removeUpload', options);\n const upload = this.uploads.get(options.id);\n if (upload) {\n upload.controller.abort();\n this.uploads.delete(options.id);\n this.notifyListeners('events', {\n name: 'cancelled',\n id: options.id,\n payload: {},\n });\n }\n }\n async doUpload(id, options) {\n const { filePath, serverUrl, headers = {}, method = 'POST', parameters = {} } = options;\n const upload = this.uploads.get(id);\n if (!upload)\n return;\n try {\n const file = await this.getFileFromPath(filePath);\n if (!file)\n throw new Error('File not found');\n const formData = new FormData();\n formData.append('file', file);\n for (const [key, value] of Object.entries(parameters)) {\n formData.append(key, value);\n }\n const response = await fetch(serverUrl, {\n method,\n headers,\n body: method === 'PUT' ? file : formData,\n signal: upload.controller.signal,\n });\n if (!response.ok)\n throw new Error(`HTTP error! status: ${response.status}`);\n this.notifyListeners('events', {\n name: 'completed',\n id,\n payload: { statusCode: response.status },\n });\n this.uploads.delete(id);\n }\n catch (error) {\n if (error.name === 'AbortError')\n return;\n if (upload.retries > 0) {\n upload.retries--;\n console.log(`Retrying upload (retries left: ${upload.retries})`);\n setTimeout(() => this.doUpload(id, options), 1000);\n }\n else {\n this.notifyListeners('events', {\n name: 'failed',\n id,\n payload: { error: error.message },\n });\n this.uploads.delete(id);\n }\n }\n }\n async getFileFromPath(filePath) {\n // Check if the path is an IndexedDB path\n if (PathHelper.isIndexedDBPath(filePath)) {\n return this.getFileFromIndexedDB(filePath);\n }\n // Otherwise, treat it as a file path from the system\n return this.getFileFromSystem(filePath);\n }\n // Retrieve the file from IndexedDB\n async getFileFromIndexedDB(filePath) {\n // Parse the path to get the database, store name, and key\n const { database, storeName, key } = PathHelper.parseIndexedDBPath(filePath);\n try {\n // Open the IndexedDB database and access the object store\n const db = await openDB(database, 1, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n },\n });\n // Get the blob from the store\n const blob = await db.get(storeName, key);\n if (!blob) {\n console.error(`File with key \"${key}\" not found in store \"${storeName}\" in database \"${database}\"`);\n return null;\n }\n // Convert the Blob to a File object\n return new File([blob], key, { type: blob.type });\n }\n catch (error) {\n console.error('Error retrieving file from IndexedDB:', error);\n return null;\n }\n }\n // Retrieve the file from the system (local file)\n async getFileFromSystem(filePath) {\n try {\n // This is a simplified version. In a real-world scenario,\n // you might need to handle different types of paths or use a file system API.\n const response = await fetch(filePath);\n const blob = await response.blob();\n return new File([blob], filePath.split('/').pop() || 'file', {\n type: blob.type,\n });\n }\n catch (error) {\n console.error('Error getting file from system:', error);\n return null;\n }\n }\n async getPluginVersion() {\n return { version: 'web' };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin","openDB"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/PathHelper.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst Uploader = registerPlugin('Uploader', {\n web: () => import('./web').then((m) => new m.UploaderWeb()),\n});\nexport * from './definitions';\nexport { Uploader };\n//# sourceMappingURL=index.js.map","export class PathHelper {\n // Check if the path follows the idb://[databaseName]/[storeName]/[key] format\n static isIndexedDBPath(path) {\n const regex = /^idb:\\/\\/([^/]+)\\/([^/]+)\\/(.+)$/;\n return regex.test(path);\n }\n // Parse the path to extract database, storeName, and key\n static parseIndexedDBPath(path) {\n const regex = /^idb:\\/\\/([^/]+)\\/([^/]+)\\/(.+)$/;\n const match = path.match(regex);\n if (!match) {\n throw new Error('Invalid IndexedDB path format');\n }\n return {\n database: match[1],\n storeName: match[2],\n key: match[3],\n };\n }\n}\n//# sourceMappingURL=PathHelper.js.map","import { WebPlugin } from '@capacitor/core';\nimport { openDB } from 'idb';\nimport { PathHelper } from './PathHelper';\nexport class UploaderWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.uploads = new Map();\n }\n async startUpload(options) {\n console.log('startUpload', options);\n const id = Math.random().toString(36).substring(2, 15);\n const controller = new AbortController();\n const maxRetries = options.maxRetries || 3;\n this.uploads.set(id, { controller, retries: maxRetries });\n this.doUpload(id, options);\n return { id };\n }\n async removeUpload(options) {\n console.log('removeUpload', options);\n const upload = this.uploads.get(options.id);\n if (upload) {\n upload.controller.abort();\n this.uploads.delete(options.id);\n this.notifyListeners('events', {\n name: 'cancelled',\n id: options.id,\n payload: {},\n });\n }\n }\n async doUpload(id, options) {\n var _a, _b, _c;\n const { serverUrl, headers = {}, method = 'POST', parameters = {} } = options;\n const upload = this.uploads.get(id);\n if (!upload)\n return;\n try {\n const files = this.normalizeFiles(options);\n if (files.length === 0)\n throw new Error('Missing required parameter: filePath or files');\n const resolvedMethod = method.toUpperCase();\n const uploadType = (_a = options.uploadType) !== null && _a !== void 0 ? _a : (resolvedMethod === 'PUT' ? 'binary' : 'multipart');\n let body;\n if (resolvedMethod === 'PUT' || uploadType === 'binary') {\n if (files.length !== 1)\n throw new Error('Binary uploads only support a single file');\n const file = await this.getFileFromPath(files[0].filePath);\n if (!file)\n throw new Error('File not found');\n body = file;\n }\n else {\n const formData = new FormData();\n for (const fileOption of files) {\n const file = await this.getFileFromPath(fileOption.filePath);\n if (!file)\n throw new Error('File not found');\n const fieldName = (_c = (_b = fileOption.fieldName) !== null && _b !== void 0 ? _b : options.fileField) !== null && _c !== void 0 ? _c : 'file';\n formData.append(fieldName, file);\n }\n for (const [key, value] of Object.entries(parameters)) {\n formData.append(key, value);\n }\n body = formData;\n }\n const response = await fetch(serverUrl, {\n method: resolvedMethod,\n headers,\n body,\n signal: upload.controller.signal,\n });\n if (!response.ok)\n throw new Error(`HTTP error! status: ${response.status}`);\n this.notifyListeners('events', {\n name: 'completed',\n id,\n payload: { statusCode: response.status },\n });\n this.uploads.delete(id);\n }\n catch (error) {\n if (error.name === 'AbortError')\n return;\n if (upload.retries > 0) {\n upload.retries--;\n console.log(`Retrying upload (retries left: ${upload.retries})`);\n setTimeout(() => this.doUpload(id, options), 1000);\n }\n else {\n this.notifyListeners('events', {\n name: 'failed',\n id,\n payload: { error: error.message },\n });\n this.uploads.delete(id);\n }\n }\n }\n normalizeFiles(options) {\n if (options.files && options.files.length > 0)\n return options.files;\n if (options.filePath) {\n return [\n {\n filePath: options.filePath,\n fieldName: options.fileField,\n mimeType: options.mimeType,\n },\n ];\n }\n return [];\n }\n async getFileFromPath(filePath) {\n // Check if the path is an IndexedDB path\n if (PathHelper.isIndexedDBPath(filePath)) {\n return this.getFileFromIndexedDB(filePath);\n }\n // Otherwise, treat it as a file path from the system\n return this.getFileFromSystem(filePath);\n }\n // Retrieve the file from IndexedDB\n async getFileFromIndexedDB(filePath) {\n // Parse the path to get the database, store name, and key\n const { database, storeName, key } = PathHelper.parseIndexedDBPath(filePath);\n try {\n // Open the IndexedDB database and access the object store\n const db = await openDB(database, 1, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n },\n });\n // Get the blob from the store\n const blob = await db.get(storeName, key);\n if (!blob) {\n console.error(`File with key \"${key}\" not found in store \"${storeName}\" in database \"${database}\"`);\n return null;\n }\n // Convert the Blob to a File object\n return new File([blob], key, { type: blob.type });\n }\n catch (error) {\n console.error('Error retrieving file from IndexedDB:', error);\n return null;\n }\n }\n // Retrieve the file from the system (local file)\n async getFileFromSystem(filePath) {\n try {\n // This is a simplified version. In a real-world scenario,\n // you might need to handle different types of paths or use a file system API.\n const response = await fetch(filePath);\n const blob = await response.blob();\n return new File([blob], filePath.split('/').pop() || 'file', {\n type: blob.type,\n });\n }\n catch (error) {\n console.error('Error getting file from system:', error);\n return null;\n }\n }\n async getPluginVersion() {\n return { version: 'web' };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin","openDB"],"mappings":"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"}
|
package/dist/plugin.js
CHANGED
|
@@ -54,23 +54,44 @@ var capacitorCapacitorUpdater = (function (exports, core, idb) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
async doUpload(id, options) {
|
|
57
|
-
|
|
57
|
+
var _a, _b, _c;
|
|
58
|
+
const { serverUrl, headers = {}, method = 'POST', parameters = {} } = options;
|
|
58
59
|
const upload = this.uploads.get(id);
|
|
59
60
|
if (!upload)
|
|
60
61
|
return;
|
|
61
62
|
try {
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
throw new Error('
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
const files = this.normalizeFiles(options);
|
|
64
|
+
if (files.length === 0)
|
|
65
|
+
throw new Error('Missing required parameter: filePath or files');
|
|
66
|
+
const resolvedMethod = method.toUpperCase();
|
|
67
|
+
const uploadType = (_a = options.uploadType) !== null && _a !== void 0 ? _a : (resolvedMethod === 'PUT' ? 'binary' : 'multipart');
|
|
68
|
+
let body;
|
|
69
|
+
if (resolvedMethod === 'PUT' || uploadType === 'binary') {
|
|
70
|
+
if (files.length !== 1)
|
|
71
|
+
throw new Error('Binary uploads only support a single file');
|
|
72
|
+
const file = await this.getFileFromPath(files[0].filePath);
|
|
73
|
+
if (!file)
|
|
74
|
+
throw new Error('File not found');
|
|
75
|
+
body = file;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
const formData = new FormData();
|
|
79
|
+
for (const fileOption of files) {
|
|
80
|
+
const file = await this.getFileFromPath(fileOption.filePath);
|
|
81
|
+
if (!file)
|
|
82
|
+
throw new Error('File not found');
|
|
83
|
+
const fieldName = (_c = (_b = fileOption.fieldName) !== null && _b !== void 0 ? _b : options.fileField) !== null && _c !== void 0 ? _c : 'file';
|
|
84
|
+
formData.append(fieldName, file);
|
|
85
|
+
}
|
|
86
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
87
|
+
formData.append(key, value);
|
|
88
|
+
}
|
|
89
|
+
body = formData;
|
|
69
90
|
}
|
|
70
91
|
const response = await fetch(serverUrl, {
|
|
71
|
-
method,
|
|
92
|
+
method: resolvedMethod,
|
|
72
93
|
headers,
|
|
73
|
-
body
|
|
94
|
+
body,
|
|
74
95
|
signal: upload.controller.signal,
|
|
75
96
|
});
|
|
76
97
|
if (!response.ok)
|
|
@@ -100,6 +121,20 @@ var capacitorCapacitorUpdater = (function (exports, core, idb) {
|
|
|
100
121
|
}
|
|
101
122
|
}
|
|
102
123
|
}
|
|
124
|
+
normalizeFiles(options) {
|
|
125
|
+
if (options.files && options.files.length > 0)
|
|
126
|
+
return options.files;
|
|
127
|
+
if (options.filePath) {
|
|
128
|
+
return [
|
|
129
|
+
{
|
|
130
|
+
filePath: options.filePath,
|
|
131
|
+
fieldName: options.fileField,
|
|
132
|
+
mimeType: options.mimeType,
|
|
133
|
+
},
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
103
138
|
async getFileFromPath(filePath) {
|
|
104
139
|
// Check if the path is an IndexedDB path
|
|
105
140
|
if (PathHelper.isIndexedDBPath(filePath)) {
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/PathHelper.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst Uploader = registerPlugin('Uploader', {\n web: () => import('./web').then((m) => new m.UploaderWeb()),\n});\nexport * from './definitions';\nexport { Uploader };\n//# sourceMappingURL=index.js.map","export class PathHelper {\n // Check if the path follows the idb://[databaseName]/[storeName]/[key] format\n static isIndexedDBPath(path) {\n const regex = /^idb:\\/\\/([^/]+)\\/([^/]+)\\/(.+)$/;\n return regex.test(path);\n }\n // Parse the path to extract database, storeName, and key\n static parseIndexedDBPath(path) {\n const regex = /^idb:\\/\\/([^/]+)\\/([^/]+)\\/(.+)$/;\n const match = path.match(regex);\n if (!match) {\n throw new Error('Invalid IndexedDB path format');\n }\n return {\n database: match[1],\n storeName: match[2],\n key: match[3],\n };\n }\n}\n//# sourceMappingURL=PathHelper.js.map","import { WebPlugin } from '@capacitor/core';\nimport { openDB } from 'idb';\nimport { PathHelper } from './PathHelper';\nexport class UploaderWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.uploads = new Map();\n }\n async startUpload(options) {\n console.log('startUpload', options);\n const id = Math.random().toString(36).substring(2, 15);\n const controller = new AbortController();\n const maxRetries = options.maxRetries || 3;\n this.uploads.set(id, { controller, retries: maxRetries });\n this.doUpload(id, options);\n return { id };\n }\n async removeUpload(options) {\n console.log('removeUpload', options);\n const upload = this.uploads.get(options.id);\n if (upload) {\n upload.controller.abort();\n this.uploads.delete(options.id);\n this.notifyListeners('events', {\n name: 'cancelled',\n id: options.id,\n payload: {},\n });\n }\n }\n async doUpload(id, options) {\n const { filePath, serverUrl, headers = {}, method = 'POST', parameters = {} } = options;\n const upload = this.uploads.get(id);\n if (!upload)\n return;\n try {\n const file = await this.getFileFromPath(filePath);\n if (!file)\n throw new Error('File not found');\n const formData = new FormData();\n formData.append('file', file);\n for (const [key, value] of Object.entries(parameters)) {\n formData.append(key, value);\n }\n const response = await fetch(serverUrl, {\n method,\n headers,\n body: method === 'PUT' ? file : formData,\n signal: upload.controller.signal,\n });\n if (!response.ok)\n throw new Error(`HTTP error! status: ${response.status}`);\n this.notifyListeners('events', {\n name: 'completed',\n id,\n payload: { statusCode: response.status },\n });\n this.uploads.delete(id);\n }\n catch (error) {\n if (error.name === 'AbortError')\n return;\n if (upload.retries > 0) {\n upload.retries--;\n console.log(`Retrying upload (retries left: ${upload.retries})`);\n setTimeout(() => this.doUpload(id, options), 1000);\n }\n else {\n this.notifyListeners('events', {\n name: 'failed',\n id,\n payload: { error: error.message },\n });\n this.uploads.delete(id);\n }\n }\n }\n async getFileFromPath(filePath) {\n // Check if the path is an IndexedDB path\n if (PathHelper.isIndexedDBPath(filePath)) {\n return this.getFileFromIndexedDB(filePath);\n }\n // Otherwise, treat it as a file path from the system\n return this.getFileFromSystem(filePath);\n }\n // Retrieve the file from IndexedDB\n async getFileFromIndexedDB(filePath) {\n // Parse the path to get the database, store name, and key\n const { database, storeName, key } = PathHelper.parseIndexedDBPath(filePath);\n try {\n // Open the IndexedDB database and access the object store\n const db = await openDB(database, 1, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n },\n });\n // Get the blob from the store\n const blob = await db.get(storeName, key);\n if (!blob) {\n console.error(`File with key \"${key}\" not found in store \"${storeName}\" in database \"${database}\"`);\n return null;\n }\n // Convert the Blob to a File object\n return new File([blob], key, { type: blob.type });\n }\n catch (error) {\n console.error('Error retrieving file from IndexedDB:', error);\n return null;\n }\n }\n // Retrieve the file from the system (local file)\n async getFileFromSystem(filePath) {\n try {\n // This is a simplified version. In a real-world scenario,\n // you might need to handle different types of paths or use a file system API.\n const response = await fetch(filePath);\n const blob = await response.blob();\n return new File([blob], filePath.split('/').pop() || 'file', {\n type: blob.type,\n });\n }\n catch (error) {\n console.error('Error getting file from system:', error);\n return null;\n }\n }\n async getPluginVersion() {\n return { version: 'web' };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin","openDB"],"mappings":";;;AACK,UAAC,QAAQ,GAAGA,mBAAc,CAAC,UAAU,EAAE;IAC5C,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/D,CAAC;;ICHM,MAAM,UAAU,CAAC;IACxB;IACA,IAAI,OAAO,eAAe,CAAC,IAAI,EAAE;IACjC,QAAQ,MAAM,KAAK,GAAG,kCAAkC;IACxD,QAAQ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;IAC/B,IAAI;IACJ;IACA,IAAI,OAAO,kBAAkB,CAAC,IAAI,EAAE;IACpC,QAAQ,MAAM,KAAK,GAAG,kCAAkC;IACxD,QAAQ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IACvC,QAAQ,IAAI,CAAC,KAAK,EAAE;IACpB,YAAY,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC;IAC5D,QAAQ;IACR,QAAQ,OAAO;IACf,YAAY,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;IAC9B,YAAY,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IAC/B,YAAY,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;IACzB,SAAS;IACT,IAAI;IACJ;;IChBO,MAAM,WAAW,SAASC,cAAS,CAAC;IAC3C,IAAI,WAAW,GAAG;IAClB,QAAQ,KAAK,CAAC,GAAG,SAAS,CAAC;IAC3B,QAAQ,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE;IAChC,IAAI;IACJ,IAAI,MAAM,WAAW,CAAC,OAAO,EAAE;IAC/B,QAAQ,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC;IAC3C,QAAQ,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC;IAC9D,QAAQ,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE;IAChD,QAAQ,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC;IAClD,QAAQ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IACjE,QAAQ,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IAClC,QAAQ,OAAO,EAAE,EAAE,EAAE;IACrB,IAAI;IACJ,IAAI,MAAM,YAAY,CAAC,OAAO,EAAE;IAChC,QAAQ,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC;IAC5C,QAAQ,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;IACnD,QAAQ,IAAI,MAAM,EAAE;IACpB,YAAY,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE;IACrC,YAAY,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;IAC3C,YAAY,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE;IAC3C,gBAAgB,IAAI,EAAE,WAAW;IACjC,gBAAgB,EAAE,EAAE,OAAO,CAAC,EAAE;IAC9B,gBAAgB,OAAO,EAAE,EAAE;IAC3B,aAAa,CAAC;IACd,QAAQ;IACR,IAAI;IACJ,IAAI,MAAM,QAAQ,CAAC,EAAE,EAAE,OAAO,EAAE;IAChC,QAAQ,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,GAAG,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,UAAU,GAAG,EAAE,EAAE,GAAG,OAAO;IAC/F,QAAQ,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3C,QAAQ,IAAI,CAAC,MAAM;IACnB,YAAY;IACZ,QAAQ,IAAI;IACZ,YAAY,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC;IAC7D,YAAY,IAAI,CAAC,IAAI;IACrB,gBAAgB,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC;IACjD,YAAY,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE;IAC3C,YAAY,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC;IACzC,YAAY,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE;IACnE,gBAAgB,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC;IAC3C,YAAY;IACZ,YAAY,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;IACpD,gBAAgB,MAAM;IACtB,gBAAgB,OAAO;IACvB,gBAAgB,IAAI,EAAE,MAAM,KAAK,KAAK,GAAG,IAAI,GAAG,QAAQ;IACxD,gBAAgB,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM;IAChD,aAAa,CAAC;IACd,YAAY,IAAI,CAAC,QAAQ,CAAC,EAAE;IAC5B,gBAAgB,MAAM,IAAI,KAAK,CAAC,CAAC,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACzE,YAAY,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE;IAC3C,gBAAgB,IAAI,EAAE,WAAW;IACjC,gBAAgB,EAAE;IAClB,gBAAgB,OAAO,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE;IACxD,aAAa,CAAC;IACd,YAAY,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;IACnC,QAAQ;IACR,QAAQ,OAAO,KAAK,EAAE;IACtB,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;IAC3C,gBAAgB;IAChB,YAAY,IAAI,MAAM,CAAC,OAAO,GAAG,CAAC,EAAE;IACpC,gBAAgB,MAAM,CAAC,OAAO,EAAE;IAChC,gBAAgB,OAAO,CAAC,GAAG,CAAC,CAAC,+BAA+B,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAChF,gBAAgB,UAAU,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC;IAClE,YAAY;IACZ,iBAAiB;IACjB,gBAAgB,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE;IAC/C,oBAAoB,IAAI,EAAE,QAAQ;IAClC,oBAAoB,EAAE;IACtB,oBAAoB,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE;IACrD,iBAAiB,CAAC;IAClB,gBAAgB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;IACvC,YAAY;IACZ,QAAQ;IACR,IAAI;IACJ,IAAI,MAAM,eAAe,CAAC,QAAQ,EAAE;IACpC;IACA,QAAQ,IAAI,UAAU,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE;IAClD,YAAY,OAAO,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC;IACtD,QAAQ;IACR;IACA,QAAQ,OAAO,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC;IAC/C,IAAI;IACJ;IACA,IAAI,MAAM,oBAAoB,CAAC,QAAQ,EAAE;IACzC;IACA,QAAQ,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,UAAU,CAAC,kBAAkB,CAAC,QAAQ,CAAC;IACpF,QAAQ,IAAI;IACZ;IACA,YAAY,MAAM,EAAE,GAAG,MAAMC,UAAM,CAAC,QAAQ,EAAE,CAAC,EAAE;IACjD,gBAAgB,OAAO,CAAC,EAAE,EAAE;IAC5B,oBAAoB,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE;IAClE,wBAAwB,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;IACvD,oBAAoB;IACpB,gBAAgB,CAAC;IACjB,aAAa,CAAC;IACd;IACA,YAAY,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC;IACrD,YAAY,IAAI,CAAC,IAAI,EAAE;IACvB,gBAAgB,OAAO,CAAC,KAAK,CAAC,CAAC,eAAe,EAAE,GAAG,CAAC,sBAAsB,EAAE,SAAS,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnH,gBAAgB,OAAO,IAAI;IAC3B,YAAY;IACZ;IACA,YAAY,OAAO,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IAC7D,QAAQ;IACR,QAAQ,OAAO,KAAK,EAAE;IACtB,YAAY,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC;IACzE,YAAY,OAAO,IAAI;IACvB,QAAQ;IACR,IAAI;IACJ;IACA,IAAI,MAAM,iBAAiB,CAAC,QAAQ,EAAE;IACtC,QAAQ,IAAI;IACZ;IACA;IACA,YAAY,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC;IAClD,YAAY,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE;IAC9C,YAAY,OAAO,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,MAAM,EAAE;IACzE,gBAAgB,IAAI,EAAE,IAAI,CAAC,IAAI;IAC/B,aAAa,CAAC;IACd,QAAQ;IACR,QAAQ,OAAO,KAAK,EAAE;IACtB,YAAY,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,CAAC;IACnE,YAAY,OAAO,IAAI;IACvB,QAAQ;IACR,IAAI;IACJ,IAAI,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;IACjC,IAAI;IACJ;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/PathHelper.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst Uploader = registerPlugin('Uploader', {\n web: () => import('./web').then((m) => new m.UploaderWeb()),\n});\nexport * from './definitions';\nexport { Uploader };\n//# sourceMappingURL=index.js.map","export class PathHelper {\n // Check if the path follows the idb://[databaseName]/[storeName]/[key] format\n static isIndexedDBPath(path) {\n const regex = /^idb:\\/\\/([^/]+)\\/([^/]+)\\/(.+)$/;\n return regex.test(path);\n }\n // Parse the path to extract database, storeName, and key\n static parseIndexedDBPath(path) {\n const regex = /^idb:\\/\\/([^/]+)\\/([^/]+)\\/(.+)$/;\n const match = path.match(regex);\n if (!match) {\n throw new Error('Invalid IndexedDB path format');\n }\n return {\n database: match[1],\n storeName: match[2],\n key: match[3],\n };\n }\n}\n//# sourceMappingURL=PathHelper.js.map","import { WebPlugin } from '@capacitor/core';\nimport { openDB } from 'idb';\nimport { PathHelper } from './PathHelper';\nexport class UploaderWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.uploads = new Map();\n }\n async startUpload(options) {\n console.log('startUpload', options);\n const id = Math.random().toString(36).substring(2, 15);\n const controller = new AbortController();\n const maxRetries = options.maxRetries || 3;\n this.uploads.set(id, { controller, retries: maxRetries });\n this.doUpload(id, options);\n return { id };\n }\n async removeUpload(options) {\n console.log('removeUpload', options);\n const upload = this.uploads.get(options.id);\n if (upload) {\n upload.controller.abort();\n this.uploads.delete(options.id);\n this.notifyListeners('events', {\n name: 'cancelled',\n id: options.id,\n payload: {},\n });\n }\n }\n async doUpload(id, options) {\n var _a, _b, _c;\n const { serverUrl, headers = {}, method = 'POST', parameters = {} } = options;\n const upload = this.uploads.get(id);\n if (!upload)\n return;\n try {\n const files = this.normalizeFiles(options);\n if (files.length === 0)\n throw new Error('Missing required parameter: filePath or files');\n const resolvedMethod = method.toUpperCase();\n const uploadType = (_a = options.uploadType) !== null && _a !== void 0 ? _a : (resolvedMethod === 'PUT' ? 'binary' : 'multipart');\n let body;\n if (resolvedMethod === 'PUT' || uploadType === 'binary') {\n if (files.length !== 1)\n throw new Error('Binary uploads only support a single file');\n const file = await this.getFileFromPath(files[0].filePath);\n if (!file)\n throw new Error('File not found');\n body = file;\n }\n else {\n const formData = new FormData();\n for (const fileOption of files) {\n const file = await this.getFileFromPath(fileOption.filePath);\n if (!file)\n throw new Error('File not found');\n const fieldName = (_c = (_b = fileOption.fieldName) !== null && _b !== void 0 ? _b : options.fileField) !== null && _c !== void 0 ? _c : 'file';\n formData.append(fieldName, file);\n }\n for (const [key, value] of Object.entries(parameters)) {\n formData.append(key, value);\n }\n body = formData;\n }\n const response = await fetch(serverUrl, {\n method: resolvedMethod,\n headers,\n body,\n signal: upload.controller.signal,\n });\n if (!response.ok)\n throw new Error(`HTTP error! status: ${response.status}`);\n this.notifyListeners('events', {\n name: 'completed',\n id,\n payload: { statusCode: response.status },\n });\n this.uploads.delete(id);\n }\n catch (error) {\n if (error.name === 'AbortError')\n return;\n if (upload.retries > 0) {\n upload.retries--;\n console.log(`Retrying upload (retries left: ${upload.retries})`);\n setTimeout(() => this.doUpload(id, options), 1000);\n }\n else {\n this.notifyListeners('events', {\n name: 'failed',\n id,\n payload: { error: error.message },\n });\n this.uploads.delete(id);\n }\n }\n }\n normalizeFiles(options) {\n if (options.files && options.files.length > 0)\n return options.files;\n if (options.filePath) {\n return [\n {\n filePath: options.filePath,\n fieldName: options.fileField,\n mimeType: options.mimeType,\n },\n ];\n }\n return [];\n }\n async getFileFromPath(filePath) {\n // Check if the path is an IndexedDB path\n if (PathHelper.isIndexedDBPath(filePath)) {\n return this.getFileFromIndexedDB(filePath);\n }\n // Otherwise, treat it as a file path from the system\n return this.getFileFromSystem(filePath);\n }\n // Retrieve the file from IndexedDB\n async getFileFromIndexedDB(filePath) {\n // Parse the path to get the database, store name, and key\n const { database, storeName, key } = PathHelper.parseIndexedDBPath(filePath);\n try {\n // Open the IndexedDB database and access the object store\n const db = await openDB(database, 1, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n },\n });\n // Get the blob from the store\n const blob = await db.get(storeName, key);\n if (!blob) {\n console.error(`File with key \"${key}\" not found in store \"${storeName}\" in database \"${database}\"`);\n return null;\n }\n // Convert the Blob to a File object\n return new File([blob], key, { type: blob.type });\n }\n catch (error) {\n console.error('Error retrieving file from IndexedDB:', error);\n return null;\n }\n }\n // Retrieve the file from the system (local file)\n async getFileFromSystem(filePath) {\n try {\n // This is a simplified version. In a real-world scenario,\n // you might need to handle different types of paths or use a file system API.\n const response = await fetch(filePath);\n const blob = await response.blob();\n return new File([blob], filePath.split('/').pop() || 'file', {\n type: blob.type,\n });\n }\n catch (error) {\n console.error('Error getting file from system:', error);\n return null;\n }\n }\n async getPluginVersion() {\n return { version: 'web' };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin","openDB"],"mappings":"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"}
|
|
@@ -10,8 +10,14 @@ import MobileCoreServices
|
|
|
10
10
|
private var retries: [String: Int] = [:]
|
|
11
11
|
private var tempBodyFiles: [String: URL] = [:]
|
|
12
12
|
|
|
13
|
+
private struct UploadFilePart {
|
|
14
|
+
let fileUrl: URL
|
|
15
|
+
let fieldName: String
|
|
16
|
+
let mimeType: String
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
private struct UploadConfig {
|
|
14
|
-
let filePath: String
|
|
20
|
+
let filePath: String?
|
|
15
21
|
let serverUrl: String
|
|
16
22
|
let options: [String: Any]
|
|
17
23
|
}
|
|
@@ -20,7 +26,7 @@ import MobileCoreServices
|
|
|
20
26
|
|
|
21
27
|
var eventHandler: (([String: Any]) -> Void)?
|
|
22
28
|
|
|
23
|
-
@objc public func startUpload(_ filePath: String
|
|
29
|
+
@objc public func startUpload(_ filePath: String?, _ serverUrl: String, _ options: [String: Any], maxRetries: Int = 3) async throws -> String {
|
|
24
30
|
let id = UUID().uuidString
|
|
25
31
|
print("startUpload: \(id)")
|
|
26
32
|
|
|
@@ -53,25 +59,25 @@ import MobileCoreServices
|
|
|
53
59
|
request.setValue(value, forHTTPHeaderField: key)
|
|
54
60
|
}
|
|
55
61
|
|
|
56
|
-
let
|
|
57
|
-
let
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
} else {
|
|
61
|
-
fileUrl = URL(fileURLWithPath: filePath)
|
|
62
|
-
}
|
|
63
|
-
guard fileUrl.isFileURL else {
|
|
64
|
-
throw NSError(domain: "UploaderPlugin", code: 1, userInfo: [NSLocalizedDescriptionKey: "Invalid file URL"])
|
|
65
|
-
}
|
|
66
|
-
let mimeType = config.options["mimeType"] as? String ?? guessMIMEType(from: filePath)
|
|
62
|
+
let defaultFieldName = (config.options["fileField"] as? String).flatMap { $0.isEmpty ? nil : $0 } ?? "file"
|
|
63
|
+
let fileParts = try resolveUploadFiles(config: config, defaultFieldName: defaultFieldName)
|
|
64
|
+
|
|
65
|
+
let uploadType = (config.options["uploadType"] as? String)?.lowercased() ?? (request.httpMethod == "PUT" ? "binary" : "multipart")
|
|
67
66
|
|
|
68
67
|
let task: URLSessionTask
|
|
69
|
-
if request.httpMethod == "PUT" {
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
if request.httpMethod == "PUT" || uploadType == "binary" {
|
|
69
|
+
// Binary uploads (e.g. S3 presigned URL uploads)
|
|
70
|
+
guard fileParts.count == 1, let filePart = fileParts.first else {
|
|
71
|
+
throw NSError(
|
|
72
|
+
domain: "UploaderPlugin",
|
|
73
|
+
code: 2,
|
|
74
|
+
userInfo: [NSLocalizedDescriptionKey: "Binary uploads only support a single file"]
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
request.setValue(filePart.mimeType, forHTTPHeaderField: "Content-Type")
|
|
78
|
+
task = self.getUrlSession().uploadTask(with: request, fromFile: filePart.fileUrl)
|
|
73
79
|
} else {
|
|
74
|
-
//
|
|
80
|
+
// Multipart uploads
|
|
75
81
|
let boundary = UUID().uuidString
|
|
76
82
|
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
|
|
77
83
|
|
|
@@ -82,8 +88,8 @@ import MobileCoreServices
|
|
|
82
88
|
}
|
|
83
89
|
let tempDir = FileManager.default.temporaryDirectory
|
|
84
90
|
let tempFile = tempDir.appendingPathComponent("upload-\(id).tmp")
|
|
85
|
-
try writeMultipartBodyToFile(at: tempFile, parameters: parameters, fileUrl: fileUrl, mimeType: mimeType, boundary: boundary)
|
|
86
91
|
tempBodyFiles[id] = tempFile
|
|
92
|
+
try writeMultipartBodyToFile(at: tempFile, parameters: parameters, fileParts: fileParts, boundary: boundary)
|
|
87
93
|
|
|
88
94
|
task = self.getUrlSession().uploadTask(with: request, fromFile: tempFile)
|
|
89
95
|
}
|
|
@@ -120,6 +126,51 @@ import MobileCoreServices
|
|
|
120
126
|
}
|
|
121
127
|
return "application/octet-stream"
|
|
122
128
|
}
|
|
129
|
+
|
|
130
|
+
private func resolveUploadFiles(config: UploadConfig, defaultFieldName: String) throws -> [UploadFilePart] {
|
|
131
|
+
let fallbackMimeType = (config.options["mimeType"] as? String).flatMap { $0.isEmpty ? nil : $0 }
|
|
132
|
+
|
|
133
|
+
if let files = config.options["files"] as? [[String: String]], !files.isEmpty {
|
|
134
|
+
return try files.enumerated().map { (index, file) in
|
|
135
|
+
guard let filePath = file["filePath"], !filePath.isEmpty else {
|
|
136
|
+
throw NSError(
|
|
137
|
+
domain: "UploaderPlugin",
|
|
138
|
+
code: 3,
|
|
139
|
+
userInfo: [NSLocalizedDescriptionKey: "Missing required parameter: files[\(index)].filePath"]
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
let fileUrl = try resolveFileUrl(from: filePath)
|
|
143
|
+
let fieldName = (file["fieldName"]?.isEmpty == false ? file["fieldName"]! : defaultFieldName)
|
|
144
|
+
let mimeType = (file["mimeType"]?.isEmpty == false ? file["mimeType"]! : (fallbackMimeType ?? guessMIMEType(from: fileUrl.path)))
|
|
145
|
+
return UploadFilePart(fileUrl: fileUrl, fieldName: fieldName, mimeType: mimeType)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if let filePath = config.filePath, !filePath.isEmpty {
|
|
150
|
+
let fileUrl = try resolveFileUrl(from: filePath)
|
|
151
|
+
let mimeType = fallbackMimeType ?? guessMIMEType(from: fileUrl.path)
|
|
152
|
+
return [UploadFilePart(fileUrl: fileUrl, fieldName: defaultFieldName, mimeType: mimeType)]
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
throw NSError(
|
|
156
|
+
domain: "UploaderPlugin",
|
|
157
|
+
code: 4,
|
|
158
|
+
userInfo: [NSLocalizedDescriptionKey: "Missing required parameter: filePath or files"]
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private func resolveFileUrl(from filePath: String) throws -> URL {
|
|
163
|
+
let fileUrl: URL
|
|
164
|
+
if let candidateUrl = URL(string: filePath), candidateUrl.isFileURL {
|
|
165
|
+
fileUrl = candidateUrl
|
|
166
|
+
} else {
|
|
167
|
+
fileUrl = URL(fileURLWithPath: filePath)
|
|
168
|
+
}
|
|
169
|
+
guard fileUrl.isFileURL else {
|
|
170
|
+
throw NSError(domain: "UploaderPlugin", code: 1, userInfo: [NSLocalizedDescriptionKey: "Invalid file URL"])
|
|
171
|
+
}
|
|
172
|
+
return fileUrl
|
|
173
|
+
}
|
|
123
174
|
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
|
124
175
|
guard let id = task.taskDescription else { return }
|
|
125
176
|
|
|
@@ -179,7 +230,12 @@ import MobileCoreServices
|
|
|
179
230
|
}
|
|
180
231
|
|
|
181
232
|
// Writes multipart/form-data body directly to a file, streaming the file content in chunks
|
|
182
|
-
private func writeMultipartBodyToFile(
|
|
233
|
+
private func writeMultipartBodyToFile(
|
|
234
|
+
at tempFile: URL,
|
|
235
|
+
parameters: [String: String],
|
|
236
|
+
fileParts: [UploadFilePart],
|
|
237
|
+
boundary: String
|
|
238
|
+
) throws {
|
|
183
239
|
FileManager.default.createFile(atPath: tempFile.path, contents: nil)
|
|
184
240
|
let writeHandle = try FileHandle(forWritingTo: tempFile)
|
|
185
241
|
defer { try? writeHandle.close() }
|
|
@@ -194,19 +250,23 @@ import MobileCoreServices
|
|
|
194
250
|
try write("\(value)\r\n")
|
|
195
251
|
}
|
|
196
252
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
253
|
+
for filePart in fileParts {
|
|
254
|
+
try write("--\(boundary)\r\n")
|
|
255
|
+
try write("Content-Disposition: form-data; name=\"\(filePart.fieldName)\"; filename=\"\(filePart.fileUrl.lastPathComponent)\"\r\n")
|
|
256
|
+
try write("Content-Type: \(filePart.mimeType)\r\n\r\n")
|
|
257
|
+
|
|
258
|
+
let readHandle = try FileHandle(forReadingFrom: filePart.fileUrl)
|
|
259
|
+
defer { try? readHandle.close() }
|
|
260
|
+
let chunkSize = 64 * 1024
|
|
261
|
+
while true {
|
|
262
|
+
guard let chunk = try readHandle.read(upToCount: chunkSize), !chunk.isEmpty else { break }
|
|
263
|
+
try writeHandle.write(contentsOf: chunk)
|
|
264
|
+
}
|
|
200
265
|
|
|
201
|
-
|
|
202
|
-
defer { try? readHandle.close() }
|
|
203
|
-
let chunkSize = 64 * 1024
|
|
204
|
-
while true {
|
|
205
|
-
guard let chunk = try readHandle.read(upToCount: chunkSize), !chunk.isEmpty else { break }
|
|
206
|
-
try writeHandle.write(contentsOf: chunk)
|
|
266
|
+
try write("\r\n")
|
|
207
267
|
}
|
|
208
268
|
|
|
209
|
-
try write("
|
|
269
|
+
try write("--\(boundary)--\r\n")
|
|
210
270
|
}
|
|
211
271
|
}
|
|
212
272
|
|
|
@@ -3,7 +3,7 @@ import Capacitor
|
|
|
3
3
|
|
|
4
4
|
@objc(UploaderPlugin)
|
|
5
5
|
public class UploaderPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
6
|
-
private let pluginVersion: String = "8.
|
|
6
|
+
private let pluginVersion: String = "8.2.0"
|
|
7
7
|
public let identifier = "UploaderPlugin"
|
|
8
8
|
public let jsName = "Uploader"
|
|
9
9
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -20,22 +20,42 @@ public class UploaderPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
@objc func startUpload(_ call: CAPPluginCall) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return
|
|
26
|
-
}
|
|
23
|
+
let filePath = call.getString("filePath")
|
|
24
|
+
let files = call.getArray("files")
|
|
27
25
|
guard let serverUrl = call.getString("serverUrl") else {
|
|
28
26
|
call.reject("Missing required parameter: serverUrl")
|
|
29
27
|
return
|
|
30
28
|
}
|
|
29
|
+
if (filePath == nil || filePath?.isEmpty == true) && (files == nil || files?.isEmpty == true) {
|
|
30
|
+
call.reject("Missing required parameter: filePath or files")
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let headers = (call.getObject("headers") ?? [:]).compactMapValues { $0 as? String }
|
|
35
|
+
let parameters = (call.getObject("parameters") ?? [:]).compactMapValues { $0 as? String }
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
"headers":
|
|
34
|
-
"
|
|
35
|
-
"mimeType": call.getString("mimeType") as Any,
|
|
36
|
-
"parameters": call.getObject("parameters") as Any
|
|
37
|
+
var options: [String: Any] = [
|
|
38
|
+
"headers": headers,
|
|
39
|
+
"parameters": parameters
|
|
37
40
|
]
|
|
38
41
|
|
|
42
|
+
if let method = call.getString("method") { options["method"] = method }
|
|
43
|
+
if let mimeType = call.getString("mimeType") { options["mimeType"] = mimeType }
|
|
44
|
+
if let uploadType = call.getString("uploadType") { options["uploadType"] = uploadType }
|
|
45
|
+
if let fileField = call.getString("fileField") { options["fileField"] = fileField }
|
|
46
|
+
|
|
47
|
+
if let files {
|
|
48
|
+
let normalizedFiles: [[String: String]] = files.compactMap { item in
|
|
49
|
+
guard let obj = item as? JSObject else { return nil }
|
|
50
|
+
var out: [String: String] = [:]
|
|
51
|
+
if let filePath = obj["filePath"] as? String { out["filePath"] = filePath }
|
|
52
|
+
if let fieldName = obj["fieldName"] as? String { out["fieldName"] = fieldName }
|
|
53
|
+
if let mimeType = obj["mimeType"] as? String { out["mimeType"] = mimeType }
|
|
54
|
+
return out.isEmpty ? nil : out
|
|
55
|
+
}
|
|
56
|
+
if !normalizedFiles.isEmpty { options["files"] = normalizedFiles }
|
|
57
|
+
}
|
|
58
|
+
|
|
39
59
|
let maxRetries = call.getInt("maxRetries") ?? 3
|
|
40
60
|
|
|
41
61
|
Task {
|