@uploadcare/upload-client 6.0.1-alpha.8 → 6.0.1-alpha.9
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/dist/index.browser.js +179 -147
- package/dist/index.d.ts +62 -55
- package/dist/index.node.js +180 -144
- package/dist/index.react-native.js +206 -167
- package/package.json +1 -1
|
@@ -157,6 +157,26 @@ const poll = ({ check, interval = DEFAULT_INTERVAL, timeout, signal }) => new Pr
|
|
|
157
157
|
tickTimeoutId = setTimeout(tick, 0);
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
+
/*
|
|
161
|
+
Settings for future support:
|
|
162
|
+
parallelDirectUploads: 10,
|
|
163
|
+
*/
|
|
164
|
+
const defaultSettings = {
|
|
165
|
+
baseCDN: 'https://ucarecdn.com',
|
|
166
|
+
baseURL: 'https://upload.uploadcare.com',
|
|
167
|
+
maxContentLength: 50 * 1024 * 1024,
|
|
168
|
+
retryThrottledRequestMaxTimes: 1,
|
|
169
|
+
retryNetworkErrorMaxTimes: 3,
|
|
170
|
+
multipartMinFileSize: 25 * 1024 * 1024,
|
|
171
|
+
multipartChunkSize: 5 * 1024 * 1024,
|
|
172
|
+
multipartMinLastPartSize: 1024 * 1024,
|
|
173
|
+
maxConcurrentRequests: 4,
|
|
174
|
+
pollingTimeoutMilliseconds: 10000,
|
|
175
|
+
pusherKey: '79ae88bd931ea68464d9'
|
|
176
|
+
};
|
|
177
|
+
const defaultContentType = 'application/octet-stream';
|
|
178
|
+
const defaultFilename = 'original';
|
|
179
|
+
|
|
160
180
|
const request = ({ method, url, data, headers = {}, signal, onProgress }) => new Promise((resolve, reject) => {
|
|
161
181
|
const xhr = new XMLHttpRequest();
|
|
162
182
|
const requestMethod = method?.toUpperCase() || 'GET';
|
|
@@ -168,14 +188,12 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
|
|
|
168
188
|
* and https://bugs.chromium.org/p/chromium/issues/detail?id=1346628
|
|
169
189
|
*/
|
|
170
190
|
xhr.open(requestMethod, url, true);
|
|
171
|
-
console.log(`xhr.open(${requestMethod}, ${url}, true)`);
|
|
172
191
|
if (headers) {
|
|
173
192
|
Object.entries(headers).forEach((entry) => {
|
|
174
193
|
const [key, value] = entry;
|
|
175
194
|
typeof value !== 'undefined' &&
|
|
176
195
|
!Array.isArray(value) &&
|
|
177
196
|
xhr.setRequestHeader(key, value);
|
|
178
|
-
console.log(`xhr.setRequestHeader(${key}, ${value})`);
|
|
179
197
|
});
|
|
180
198
|
}
|
|
181
199
|
xhr.responseType = 'text';
|
|
@@ -227,7 +245,6 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
|
|
|
227
245
|
xhr.onerror = (progressEvent) => {
|
|
228
246
|
if (aborted)
|
|
229
247
|
return;
|
|
230
|
-
console.log('aboirt', xhr, progressEvent);
|
|
231
248
|
// only triggers if the request couldn't be made at all
|
|
232
249
|
reject(new UploadcareNetworkError(progressEvent));
|
|
233
250
|
};
|
|
@@ -245,7 +262,6 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
|
|
|
245
262
|
};
|
|
246
263
|
}
|
|
247
264
|
if (data) {
|
|
248
|
-
console.log(`xhr.send(${data})`);
|
|
249
265
|
xhr.send(data);
|
|
250
266
|
}
|
|
251
267
|
else {
|
|
@@ -253,26 +269,28 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
|
|
|
253
269
|
}
|
|
254
270
|
});
|
|
255
271
|
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
260
|
-
return uri.startsWith('file:') || uri.startsWith('content:');
|
|
272
|
+
const isBlob = (data) => {
|
|
273
|
+
return typeof Blob !== 'undefined' && data instanceof Blob;
|
|
261
274
|
};
|
|
262
|
-
const
|
|
263
|
-
return
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
275
|
+
const isFile = (data) => {
|
|
276
|
+
return typeof File !== 'undefined' && data instanceof File;
|
|
277
|
+
};
|
|
278
|
+
const isBuffer = (data) => {
|
|
279
|
+
return typeof Buffer !== 'undefined' && data instanceof Buffer;
|
|
280
|
+
};
|
|
281
|
+
const isReactNativeAsset = (data) => {
|
|
282
|
+
return (!!data &&
|
|
283
|
+
typeof data === 'object' &&
|
|
284
|
+
!Array.isArray(data) &&
|
|
285
|
+
'uri' in data &&
|
|
286
|
+
typeof data.uri === 'string');
|
|
287
|
+
};
|
|
288
|
+
const isFileData = (data) => {
|
|
289
|
+
return (isBlob(data) || isFile(data) || isBuffer(data) || isReactNativeAsset(data));
|
|
269
290
|
};
|
|
270
291
|
|
|
271
|
-
const getFileOptions = (
|
|
292
|
+
const getFileOptions = () => [];
|
|
272
293
|
const transformFile = (file, name, contentType) => {
|
|
273
|
-
if (isReactNativeUri(file)) {
|
|
274
|
-
return { uri: file, name, type: contentType };
|
|
275
|
-
}
|
|
276
294
|
if (isReactNativeAsset(file)) {
|
|
277
295
|
return {
|
|
278
296
|
uri: file.uri,
|
|
@@ -280,41 +298,14 @@ const transformFile = (file, name, contentType) => {
|
|
|
280
298
|
type: file.type || contentType
|
|
281
299
|
};
|
|
282
300
|
}
|
|
283
|
-
|
|
284
|
-
|
|
301
|
+
if (isBlob(file)) {
|
|
302
|
+
const uri = URL.createObjectURL(file);
|
|
303
|
+
return { uri, name: name, type: file.type || contentType };
|
|
304
|
+
}
|
|
305
|
+
throw new Error(`Unsupported file type: ${file}`);
|
|
285
306
|
};
|
|
286
307
|
var getFormData = () => new FormData();
|
|
287
308
|
|
|
288
|
-
/**
|
|
289
|
-
* FileData type guard.
|
|
290
|
-
*/
|
|
291
|
-
const isFileData = (data) => {
|
|
292
|
-
return (data !== undefined &&
|
|
293
|
-
((typeof Blob !== 'undefined' && data instanceof Blob) ||
|
|
294
|
-
(typeof File !== 'undefined' && data instanceof File) ||
|
|
295
|
-
(typeof Buffer !== 'undefined' && data instanceof Buffer) ||
|
|
296
|
-
(typeof data === 'string' && isReactNativeUri(data)) ||
|
|
297
|
-
(typeof data === 'object' && isReactNativeAsset(data))));
|
|
298
|
-
};
|
|
299
|
-
/**
|
|
300
|
-
* Uuid type guard.
|
|
301
|
-
*/
|
|
302
|
-
const isUuid = (data) => {
|
|
303
|
-
const UUID_REGEX = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
|
|
304
|
-
const regExp = new RegExp(UUID_REGEX);
|
|
305
|
-
return !isFileData(data) && regExp.test(data);
|
|
306
|
-
};
|
|
307
|
-
/**
|
|
308
|
-
* Url type guard.
|
|
309
|
-
*
|
|
310
|
-
* @param {NodeFile | BrowserFile | Url | Uuid} data
|
|
311
|
-
*/
|
|
312
|
-
const isUrl = (data) => {
|
|
313
|
-
const URL_REGEX = '^(?:\\w+:)?\\/\\/([^\\s\\.]+\\.\\S{2}|localhost[\\:?\\d]*)\\S*$';
|
|
314
|
-
const regExp = new RegExp(URL_REGEX);
|
|
315
|
-
return !isFileData(data) && regExp.test(data);
|
|
316
|
-
};
|
|
317
|
-
|
|
318
309
|
const isSimpleValue = (value) => {
|
|
319
310
|
return (typeof value === 'string' ||
|
|
320
311
|
typeof value === 'number' ||
|
|
@@ -331,7 +322,7 @@ function collectParams(params, inputKey, inputValue) {
|
|
|
331
322
|
if (isFileValue(inputValue)) {
|
|
332
323
|
const { name, contentType } = inputValue;
|
|
333
324
|
const file = transformFile(inputValue.data, name, contentType); // lgtm [js/superfluous-trailing-arguments]
|
|
334
|
-
const options = getFileOptions(
|
|
325
|
+
const options = getFileOptions();
|
|
335
326
|
params.push([inputKey, file, ...options]);
|
|
336
327
|
}
|
|
337
328
|
else if (isObjectValue(inputValue)) {
|
|
@@ -353,11 +344,9 @@ function getFormDataParams(options) {
|
|
|
353
344
|
return params;
|
|
354
345
|
}
|
|
355
346
|
function buildFormData(options) {
|
|
356
|
-
console.log('buildFormData', options);
|
|
357
347
|
const formData = getFormData();
|
|
358
348
|
const paramsList = getFormDataParams(options);
|
|
359
349
|
for (const params of paramsList) {
|
|
360
|
-
console.log('params', params);
|
|
361
350
|
const [key, value, ...rest] = params;
|
|
362
351
|
// node form-data has another signature for append
|
|
363
352
|
formData.append(key, value, ...rest);
|
|
@@ -365,6 +354,19 @@ function buildFormData(options) {
|
|
|
365
354
|
return formData;
|
|
366
355
|
}
|
|
367
356
|
|
|
357
|
+
class UploadClientError extends Error {
|
|
358
|
+
constructor(message, code, request, response, headers) {
|
|
359
|
+
super();
|
|
360
|
+
this.name = 'UploadClientError';
|
|
361
|
+
this.message = message;
|
|
362
|
+
this.code = code;
|
|
363
|
+
this.request = request;
|
|
364
|
+
this.response = response;
|
|
365
|
+
this.headers = headers;
|
|
366
|
+
Object.setPrototypeOf(this, UploadClientError.prototype);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
368
370
|
const buildSearchParams = (query) => {
|
|
369
371
|
const searchParams = new URLSearchParams();
|
|
370
372
|
for (const [key, value] of Object.entries(query)) {
|
|
@@ -396,26 +398,6 @@ const getUrl = (base, path, query) => {
|
|
|
396
398
|
return url.toString();
|
|
397
399
|
};
|
|
398
400
|
|
|
399
|
-
/*
|
|
400
|
-
Settings for future support:
|
|
401
|
-
parallelDirectUploads: 10,
|
|
402
|
-
*/
|
|
403
|
-
const defaultSettings = {
|
|
404
|
-
baseCDN: 'https://ucarecdn.com',
|
|
405
|
-
baseURL: 'https://upload.uploadcare.com',
|
|
406
|
-
maxContentLength: 50 * 1024 * 1024,
|
|
407
|
-
retryThrottledRequestMaxTimes: 1,
|
|
408
|
-
retryNetworkErrorMaxTimes: 3,
|
|
409
|
-
multipartMinFileSize: 25 * 1024 * 1024,
|
|
410
|
-
multipartChunkSize: 5 * 1024 * 1024,
|
|
411
|
-
multipartMinLastPartSize: 1024 * 1024,
|
|
412
|
-
maxConcurrentRequests: 4,
|
|
413
|
-
pollingTimeoutMilliseconds: 10000,
|
|
414
|
-
pusherKey: '79ae88bd931ea68464d9'
|
|
415
|
-
};
|
|
416
|
-
const defaultContentType = 'application/octet-stream';
|
|
417
|
-
const defaultFilename = 'original';
|
|
418
|
-
|
|
419
401
|
var version = '6.0.0';
|
|
420
402
|
|
|
421
403
|
const LIBRARY_NAME = 'UploadcareUploadClient';
|
|
@@ -428,19 +410,6 @@ function getUserAgent(options) {
|
|
|
428
410
|
});
|
|
429
411
|
}
|
|
430
412
|
|
|
431
|
-
class UploadClientError extends Error {
|
|
432
|
-
constructor(message, code, request, response, headers) {
|
|
433
|
-
super();
|
|
434
|
-
this.name = 'UploadClientError';
|
|
435
|
-
this.message = message;
|
|
436
|
-
this.code = code;
|
|
437
|
-
this.request = request;
|
|
438
|
-
this.response = response;
|
|
439
|
-
this.headers = headers;
|
|
440
|
-
Object.setPrototypeOf(this, UploadClientError.prototype);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
413
|
const REQUEST_WAS_THROTTLED_CODE = 'RequestThrottledError';
|
|
445
414
|
const DEFAULT_RETRY_AFTER_TIMEOUT = 15000;
|
|
446
415
|
const DEFAULT_NETWORK_ERROR_TIMEOUT = 1000;
|
|
@@ -471,6 +440,31 @@ function retryIfFailed(fn, options) {
|
|
|
471
440
|
}));
|
|
472
441
|
}
|
|
473
442
|
|
|
443
|
+
const getContentType = (file) => {
|
|
444
|
+
if (isBlob(file) || isFile(file) || isReactNativeAsset(file)) {
|
|
445
|
+
return file.type || defaultContentType;
|
|
446
|
+
}
|
|
447
|
+
if (isBuffer(file)) {
|
|
448
|
+
return defaultFilename;
|
|
449
|
+
}
|
|
450
|
+
console.warn(`Unknown filename: ${file}. Using default filename: ${defaultFilename}`);
|
|
451
|
+
return defaultFilename;
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const getFilename = (file) => {
|
|
455
|
+
if (isBlob(file) || isBuffer(file)) {
|
|
456
|
+
return defaultFilename;
|
|
457
|
+
}
|
|
458
|
+
if (isFile(file)) {
|
|
459
|
+
return file.name || defaultFilename;
|
|
460
|
+
}
|
|
461
|
+
if (isReactNativeAsset(file)) {
|
|
462
|
+
return file.name || defaultFilename;
|
|
463
|
+
}
|
|
464
|
+
console.warn(`Unknown file type: ${file}. Using default filename: ${defaultFilename}`);
|
|
465
|
+
return defaultFilename;
|
|
466
|
+
};
|
|
467
|
+
|
|
474
468
|
function getStoreValue(store) {
|
|
475
469
|
return typeof store === 'undefined' ? 'auto' : store ? '1' : '0';
|
|
476
470
|
}
|
|
@@ -486,13 +480,13 @@ function base(file, { publicKey, fileName, contentType, baseURL = defaultSetting
|
|
|
486
480
|
jsonerrors: 1
|
|
487
481
|
}),
|
|
488
482
|
headers: {
|
|
489
|
-
'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
|
|
483
|
+
'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
|
|
490
484
|
},
|
|
491
485
|
data: buildFormData({
|
|
492
486
|
file: {
|
|
493
487
|
data: file,
|
|
494
|
-
name: fileName
|
|
495
|
-
contentType: contentType
|
|
488
|
+
name: fileName || getFilename(file),
|
|
489
|
+
contentType: contentType || getContentType(file)
|
|
496
490
|
},
|
|
497
491
|
UPLOADCARE_PUB_KEY: publicKey,
|
|
498
492
|
UPLOADCARE_STORE: getStoreValue(store),
|
|
@@ -690,9 +684,9 @@ function multipartStart(size, { publicKey, contentType, fileName, multipartChunk
|
|
|
690
684
|
'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
|
|
691
685
|
},
|
|
692
686
|
data: buildFormData({
|
|
693
|
-
filename: fileName
|
|
687
|
+
filename: fileName || defaultFilename,
|
|
694
688
|
size: size,
|
|
695
|
-
content_type: contentType
|
|
689
|
+
content_type: contentType || defaultContentType,
|
|
696
690
|
part_size: multipartChunkSize,
|
|
697
691
|
UPLOADCARE_STORE: getStoreValue(store),
|
|
698
692
|
UPLOADCARE_PUB_KEY: publicKey,
|
|
@@ -769,6 +763,28 @@ function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL,
|
|
|
769
763
|
}), { retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes });
|
|
770
764
|
}
|
|
771
765
|
|
|
766
|
+
function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes, signal, onProgress }) {
|
|
767
|
+
return poll({
|
|
768
|
+
check: (signal) => info(file, {
|
|
769
|
+
publicKey,
|
|
770
|
+
baseURL,
|
|
771
|
+
signal,
|
|
772
|
+
source,
|
|
773
|
+
integration,
|
|
774
|
+
userAgent,
|
|
775
|
+
retryThrottledRequestMaxTimes,
|
|
776
|
+
retryNetworkErrorMaxTimes
|
|
777
|
+
}).then((response) => {
|
|
778
|
+
if (response.isReady) {
|
|
779
|
+
return response;
|
|
780
|
+
}
|
|
781
|
+
onProgress && onProgress({ isComputable: true, value: 1 });
|
|
782
|
+
return false;
|
|
783
|
+
}),
|
|
784
|
+
signal
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
|
|
772
788
|
class UploadcareFile {
|
|
773
789
|
constructor(fileInfo, { baseCDN, fileName }) {
|
|
774
790
|
this.name = null;
|
|
@@ -806,28 +822,6 @@ class UploadcareFile {
|
|
|
806
822
|
}
|
|
807
823
|
}
|
|
808
824
|
|
|
809
|
-
function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes, signal, onProgress }) {
|
|
810
|
-
return poll({
|
|
811
|
-
check: (signal) => info(file, {
|
|
812
|
-
publicKey,
|
|
813
|
-
baseURL,
|
|
814
|
-
signal,
|
|
815
|
-
source,
|
|
816
|
-
integration,
|
|
817
|
-
userAgent,
|
|
818
|
-
retryThrottledRequestMaxTimes,
|
|
819
|
-
retryNetworkErrorMaxTimes
|
|
820
|
-
}).then((response) => {
|
|
821
|
-
if (response.isReady) {
|
|
822
|
-
return response;
|
|
823
|
-
}
|
|
824
|
-
onProgress && onProgress({ isComputable: true, value: 1 });
|
|
825
|
-
return false;
|
|
826
|
-
}),
|
|
827
|
-
signal
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
|
|
831
825
|
const uploadDirect = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, contentType, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes, baseCDN, metadata }) => {
|
|
832
826
|
return base(file, {
|
|
833
827
|
publicKey,
|
|
@@ -863,6 +857,29 @@ const uploadDirect = (file, { publicKey, fileName, baseURL, secureSignature, sec
|
|
|
863
857
|
.then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
|
|
864
858
|
};
|
|
865
859
|
|
|
860
|
+
const uploadFromUploaded = (uuid, { publicKey, fileName, baseURL, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes, baseCDN }) => {
|
|
861
|
+
return info(uuid, {
|
|
862
|
+
publicKey,
|
|
863
|
+
baseURL,
|
|
864
|
+
signal,
|
|
865
|
+
source,
|
|
866
|
+
integration,
|
|
867
|
+
userAgent,
|
|
868
|
+
retryThrottledRequestMaxTimes,
|
|
869
|
+
retryNetworkErrorMaxTimes
|
|
870
|
+
})
|
|
871
|
+
.then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN, fileName }))
|
|
872
|
+
.then((result) => {
|
|
873
|
+
// hack for node ¯\_(ツ)_/¯
|
|
874
|
+
if (onProgress)
|
|
875
|
+
onProgress({
|
|
876
|
+
isComputable: true,
|
|
877
|
+
value: 1
|
|
878
|
+
});
|
|
879
|
+
return result;
|
|
880
|
+
});
|
|
881
|
+
};
|
|
882
|
+
|
|
866
883
|
const race = (fns, { signal } = {}) => {
|
|
867
884
|
let lastError = null;
|
|
868
885
|
let winnerIndex = null;
|
|
@@ -1202,35 +1219,30 @@ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, check
|
|
|
1202
1219
|
}))
|
|
1203
1220
|
.then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
|
|
1204
1221
|
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
retryThrottledRequestMaxTimes,
|
|
1214
|
-
retryNetworkErrorMaxTimes
|
|
1215
|
-
})
|
|
1216
|
-
.then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN, fileName }))
|
|
1217
|
-
.then((result) => {
|
|
1218
|
-
// hack for node ¯\_(ツ)_/¯
|
|
1219
|
-
if (onProgress)
|
|
1220
|
-
onProgress({
|
|
1221
|
-
isComputable: true,
|
|
1222
|
-
value: 1
|
|
1223
|
-
});
|
|
1224
|
-
return result;
|
|
1225
|
-
});
|
|
1222
|
+
const memo = new WeakMap();
|
|
1223
|
+
const getReactNativeBlob = async (asset) => {
|
|
1224
|
+
if (memo.has(asset)) {
|
|
1225
|
+
return memo.get(asset);
|
|
1226
|
+
}
|
|
1227
|
+
const blob = await fetch(asset.uri).then((res) => res.blob());
|
|
1228
|
+
memo.set(asset, blob);
|
|
1229
|
+
return blob;
|
|
1226
1230
|
};
|
|
1227
1231
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1232
|
+
const getFileSize = async (file) => {
|
|
1233
|
+
if (isBuffer(file)) {
|
|
1234
|
+
return file.length;
|
|
1235
|
+
}
|
|
1236
|
+
if (isFile(file) || isBlob(file)) {
|
|
1237
|
+
return file.size;
|
|
1238
|
+
}
|
|
1239
|
+
if (isReactNativeAsset(file)) {
|
|
1240
|
+
const blob = await getReactNativeBlob(file);
|
|
1241
|
+
return blob.size;
|
|
1242
|
+
}
|
|
1243
|
+
throw new Error(`Failed to get file size for file: ${file}`);
|
|
1233
1244
|
};
|
|
1245
|
+
|
|
1234
1246
|
/**
|
|
1235
1247
|
* Check if FileData is multipart data.
|
|
1236
1248
|
*/
|
|
@@ -1238,28 +1250,24 @@ const isMultipart = (fileSize, multipartMinFileSize = defaultSettings.multipartM
|
|
|
1238
1250
|
return fileSize >= multipartMinFileSize;
|
|
1239
1251
|
};
|
|
1240
1252
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1253
|
+
/**
|
|
1254
|
+
* Uuid type guard.
|
|
1255
|
+
*/
|
|
1256
|
+
const isUuid = (data) => {
|
|
1257
|
+
const UUID_REGEX = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
|
|
1258
|
+
const regExp = new RegExp(UUID_REGEX);
|
|
1259
|
+
return !isFileData(data) && regExp.test(data);
|
|
1245
1260
|
};
|
|
1246
|
-
|
|
1247
1261
|
/**
|
|
1248
|
-
*
|
|
1249
|
-
*
|
|
1250
|
-
* We need to store references to sliced blobs to prevent source blob from
|
|
1251
|
-
* being deallocated until uploading complete. Access to deallocated blob
|
|
1252
|
-
* causes app crash.
|
|
1262
|
+
* Url type guard.
|
|
1253
1263
|
*
|
|
1254
|
-
*
|
|
1255
|
-
* and https://github.com/facebook/react-native/issues/27543
|
|
1264
|
+
* @param {AnyFile | Url | Uuid} data
|
|
1256
1265
|
*/
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
}
|
|
1266
|
+
const isUrl = (data) => {
|
|
1267
|
+
const URL_REGEX = '^(?:\\w+:)?\\/\\/([^\\s\\.]+\\.\\S{2}|localhost[\\:?\\d]*)\\S*$';
|
|
1268
|
+
const regExp = new RegExp(URL_REGEX);
|
|
1269
|
+
return !isFileData(data) && regExp.test(data);
|
|
1270
|
+
};
|
|
1263
1271
|
|
|
1264
1272
|
const runWithConcurrency = (concurrency, tasks) => {
|
|
1265
1273
|
return new Promise((resolve, reject) => {
|
|
@@ -1296,6 +1304,36 @@ const runWithConcurrency = (concurrency, tasks) => {
|
|
|
1296
1304
|
});
|
|
1297
1305
|
};
|
|
1298
1306
|
|
|
1307
|
+
const sliceChunk = (file, index, fileSize, chunkSize) => {
|
|
1308
|
+
const start = chunkSize * index;
|
|
1309
|
+
const end = Math.min(start + chunkSize, fileSize);
|
|
1310
|
+
return file.slice(start, end);
|
|
1311
|
+
};
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* React-native hack for blob slicing
|
|
1315
|
+
*
|
|
1316
|
+
* We need to store references to sliced blobs to prevent source blob from
|
|
1317
|
+
* being deallocated until uploading complete. Access to deallocated blob
|
|
1318
|
+
* causes app crash.
|
|
1319
|
+
*
|
|
1320
|
+
* See https://github.com/uploadcare/uploadcare-js-api-clients/issues/306
|
|
1321
|
+
* and https://github.com/facebook/react-native/issues/27543
|
|
1322
|
+
*/
|
|
1323
|
+
const prepareChunks = async (file, fileSize, chunkSize) => {
|
|
1324
|
+
let blob;
|
|
1325
|
+
if (isReactNativeAsset(file)) {
|
|
1326
|
+
blob = await getReactNativeBlob(file);
|
|
1327
|
+
}
|
|
1328
|
+
else {
|
|
1329
|
+
blob = file;
|
|
1330
|
+
}
|
|
1331
|
+
return (index) => {
|
|
1332
|
+
const chunk = sliceChunk(blob, index, fileSize, chunkSize);
|
|
1333
|
+
return chunk;
|
|
1334
|
+
};
|
|
1335
|
+
};
|
|
1336
|
+
|
|
1299
1337
|
const uploadPart = (chunk, url, { publicKey, onProgress, signal, integration, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes }) => multipartUpload(chunk, url, {
|
|
1300
1338
|
publicKey,
|
|
1301
1339
|
onProgress,
|
|
@@ -1304,8 +1342,8 @@ const uploadPart = (chunk, url, { publicKey, onProgress, signal, integration, re
|
|
|
1304
1342
|
retryThrottledRequestMaxTimes,
|
|
1305
1343
|
retryNetworkErrorMaxTimes
|
|
1306
1344
|
});
|
|
1307
|
-
const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, maxConcurrentRequests = defaultSettings.maxConcurrentRequests, baseCDN, metadata }) => {
|
|
1308
|
-
const size = fileSize || getFileSize(file);
|
|
1345
|
+
const uploadMultipart = async (file, { publicKey, fileName, fileSize, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, maxConcurrentRequests = defaultSettings.maxConcurrentRequests, baseCDN, metadata }) => {
|
|
1346
|
+
const size = fileSize || await getFileSize(file);
|
|
1309
1347
|
let progressValues;
|
|
1310
1348
|
const createProgressHandler = (totalChunks, chunkIdx) => {
|
|
1311
1349
|
if (!onProgress)
|
|
@@ -1327,8 +1365,8 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
|
|
|
1327
1365
|
};
|
|
1328
1366
|
return multipartStart(size, {
|
|
1329
1367
|
publicKey,
|
|
1330
|
-
contentType,
|
|
1331
|
-
fileName: fileName
|
|
1368
|
+
contentType: contentType || getContentType(file),
|
|
1369
|
+
fileName: fileName || getFilename(file),
|
|
1332
1370
|
baseURL,
|
|
1333
1371
|
secureSignature,
|
|
1334
1372
|
secureExpire,
|
|
@@ -1341,8 +1379,8 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
|
|
|
1341
1379
|
retryNetworkErrorMaxTimes,
|
|
1342
1380
|
metadata
|
|
1343
1381
|
})
|
|
1344
|
-
.then(({ uuid, parts }) => {
|
|
1345
|
-
const getChunk = prepareChunks(file, size, multipartChunkSize);
|
|
1382
|
+
.then(async ({ uuid, parts }) => {
|
|
1383
|
+
const getChunk = await prepareChunks(file, size, multipartChunkSize);
|
|
1346
1384
|
return Promise.all([
|
|
1347
1385
|
uuid,
|
|
1348
1386
|
runWithConcurrency(maxConcurrentRequests, parts.map((url, index) => () => uploadPart(getChunk(index), url, {
|
|
@@ -1389,14 +1427,15 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
|
|
|
1389
1427
|
/**
|
|
1390
1428
|
* Uploads file from provided data.
|
|
1391
1429
|
*/
|
|
1392
|
-
function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes, contentType, multipartMinFileSize, multipartChunkSize, maxConcurrentRequests, baseCDN = defaultSettings.baseCDN, checkForUrlDuplicates, saveUrlForRecurrentUploads, pusherKey, metadata }) {
|
|
1430
|
+
async function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes, contentType, multipartMinFileSize, multipartChunkSize, maxConcurrentRequests, baseCDN = defaultSettings.baseCDN, checkForUrlDuplicates, saveUrlForRecurrentUploads, pusherKey, metadata }) {
|
|
1393
1431
|
if (isFileData(data)) {
|
|
1394
|
-
const fileSize = getFileSize(data);
|
|
1432
|
+
const fileSize = await getFileSize(data);
|
|
1395
1433
|
if (isMultipart(fileSize, multipartMinFileSize)) {
|
|
1396
1434
|
return uploadMultipart(data, {
|
|
1397
1435
|
publicKey,
|
|
1398
1436
|
contentType,
|
|
1399
1437
|
multipartChunkSize,
|
|
1438
|
+
fileSize,
|
|
1400
1439
|
fileName,
|
|
1401
1440
|
baseURL,
|
|
1402
1441
|
secureSignature,
|