@uploadcare/upload-client 6.0.1-alpha.7 → 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 -143
- package/dist/index.d.ts +62 -55
- package/dist/index.node.js +180 -140
- package/dist/index.react-native.js +211 -165
- 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,61 +269,43 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
|
|
|
253
269
|
}
|
|
254
270
|
});
|
|
255
271
|
|
|
256
|
-
const
|
|
257
|
-
return
|
|
272
|
+
const isBlob = (data) => {
|
|
273
|
+
return typeof Blob !== 'undefined' && data instanceof Blob;
|
|
274
|
+
};
|
|
275
|
+
const isFile = (data) => {
|
|
276
|
+
return typeof File !== 'undefined' && data instanceof File;
|
|
258
277
|
};
|
|
259
|
-
const
|
|
260
|
-
return
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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));
|
|
265
290
|
};
|
|
266
291
|
|
|
267
|
-
const getFileOptions = (
|
|
292
|
+
const getFileOptions = () => [];
|
|
268
293
|
const transformFile = (file, name, contentType) => {
|
|
269
|
-
if (typeof file === 'string' && isReactNativeUri(file)) {
|
|
270
|
-
return { uri: file, name, type: contentType };
|
|
271
|
-
}
|
|
272
294
|
if (isReactNativeAsset(file)) {
|
|
273
|
-
return
|
|
295
|
+
return {
|
|
296
|
+
uri: file.uri,
|
|
297
|
+
name: file.name || name,
|
|
298
|
+
type: file.type || contentType
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (isBlob(file)) {
|
|
302
|
+
const uri = URL.createObjectURL(file);
|
|
303
|
+
return { uri, name: name, type: file.type || contentType };
|
|
274
304
|
}
|
|
275
|
-
|
|
276
|
-
const type = contentType || file.type;
|
|
277
|
-
return { uri, name, type };
|
|
305
|
+
throw new Error(`Unsupported file type: ${file}`);
|
|
278
306
|
};
|
|
279
307
|
var getFormData = () => new FormData();
|
|
280
308
|
|
|
281
|
-
/**
|
|
282
|
-
* FileData type guard.
|
|
283
|
-
*/
|
|
284
|
-
const isFileData = (data) => {
|
|
285
|
-
return (data !== undefined &&
|
|
286
|
-
((typeof Blob !== 'undefined' && data instanceof Blob) ||
|
|
287
|
-
(typeof File !== 'undefined' && data instanceof File) ||
|
|
288
|
-
(typeof Buffer !== 'undefined' && data instanceof Buffer) ||
|
|
289
|
-
(typeof data === 'string' && isReactNativeUri(data)) ||
|
|
290
|
-
(typeof data === 'object' && isReactNativeAsset(data))));
|
|
291
|
-
};
|
|
292
|
-
/**
|
|
293
|
-
* Uuid type guard.
|
|
294
|
-
*/
|
|
295
|
-
const isUuid = (data) => {
|
|
296
|
-
const UUID_REGEX = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
|
|
297
|
-
const regExp = new RegExp(UUID_REGEX);
|
|
298
|
-
return !isFileData(data) && regExp.test(data);
|
|
299
|
-
};
|
|
300
|
-
/**
|
|
301
|
-
* Url type guard.
|
|
302
|
-
*
|
|
303
|
-
* @param {NodeFile | BrowserFile | Url | Uuid} data
|
|
304
|
-
*/
|
|
305
|
-
const isUrl = (data) => {
|
|
306
|
-
const URL_REGEX = '^(?:\\w+:)?\\/\\/([^\\s\\.]+\\.\\S{2}|localhost[\\:?\\d]*)\\S*$';
|
|
307
|
-
const regExp = new RegExp(URL_REGEX);
|
|
308
|
-
return !isFileData(data) && regExp.test(data);
|
|
309
|
-
};
|
|
310
|
-
|
|
311
309
|
const isSimpleValue = (value) => {
|
|
312
310
|
return (typeof value === 'string' ||
|
|
313
311
|
typeof value === 'number' ||
|
|
@@ -324,7 +322,7 @@ function collectParams(params, inputKey, inputValue) {
|
|
|
324
322
|
if (isFileValue(inputValue)) {
|
|
325
323
|
const { name, contentType } = inputValue;
|
|
326
324
|
const file = transformFile(inputValue.data, name, contentType); // lgtm [js/superfluous-trailing-arguments]
|
|
327
|
-
const options = getFileOptions(
|
|
325
|
+
const options = getFileOptions();
|
|
328
326
|
params.push([inputKey, file, ...options]);
|
|
329
327
|
}
|
|
330
328
|
else if (isObjectValue(inputValue)) {
|
|
@@ -346,11 +344,9 @@ function getFormDataParams(options) {
|
|
|
346
344
|
return params;
|
|
347
345
|
}
|
|
348
346
|
function buildFormData(options) {
|
|
349
|
-
console.log('buildFormData', options);
|
|
350
347
|
const formData = getFormData();
|
|
351
348
|
const paramsList = getFormDataParams(options);
|
|
352
349
|
for (const params of paramsList) {
|
|
353
|
-
console.log('params', params);
|
|
354
350
|
const [key, value, ...rest] = params;
|
|
355
351
|
// node form-data has another signature for append
|
|
356
352
|
formData.append(key, value, ...rest);
|
|
@@ -358,6 +354,19 @@ function buildFormData(options) {
|
|
|
358
354
|
return formData;
|
|
359
355
|
}
|
|
360
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
|
+
|
|
361
370
|
const buildSearchParams = (query) => {
|
|
362
371
|
const searchParams = new URLSearchParams();
|
|
363
372
|
for (const [key, value] of Object.entries(query)) {
|
|
@@ -389,26 +398,6 @@ const getUrl = (base, path, query) => {
|
|
|
389
398
|
return url.toString();
|
|
390
399
|
};
|
|
391
400
|
|
|
392
|
-
/*
|
|
393
|
-
Settings for future support:
|
|
394
|
-
parallelDirectUploads: 10,
|
|
395
|
-
*/
|
|
396
|
-
const defaultSettings = {
|
|
397
|
-
baseCDN: 'https://ucarecdn.com',
|
|
398
|
-
baseURL: 'https://upload.uploadcare.com',
|
|
399
|
-
maxContentLength: 50 * 1024 * 1024,
|
|
400
|
-
retryThrottledRequestMaxTimes: 1,
|
|
401
|
-
retryNetworkErrorMaxTimes: 3,
|
|
402
|
-
multipartMinFileSize: 25 * 1024 * 1024,
|
|
403
|
-
multipartChunkSize: 5 * 1024 * 1024,
|
|
404
|
-
multipartMinLastPartSize: 1024 * 1024,
|
|
405
|
-
maxConcurrentRequests: 4,
|
|
406
|
-
pollingTimeoutMilliseconds: 10000,
|
|
407
|
-
pusherKey: '79ae88bd931ea68464d9'
|
|
408
|
-
};
|
|
409
|
-
const defaultContentType = 'application/octet-stream';
|
|
410
|
-
const defaultFilename = 'original';
|
|
411
|
-
|
|
412
401
|
var version = '6.0.0';
|
|
413
402
|
|
|
414
403
|
const LIBRARY_NAME = 'UploadcareUploadClient';
|
|
@@ -421,19 +410,6 @@ function getUserAgent(options) {
|
|
|
421
410
|
});
|
|
422
411
|
}
|
|
423
412
|
|
|
424
|
-
class UploadClientError extends Error {
|
|
425
|
-
constructor(message, code, request, response, headers) {
|
|
426
|
-
super();
|
|
427
|
-
this.name = 'UploadClientError';
|
|
428
|
-
this.message = message;
|
|
429
|
-
this.code = code;
|
|
430
|
-
this.request = request;
|
|
431
|
-
this.response = response;
|
|
432
|
-
this.headers = headers;
|
|
433
|
-
Object.setPrototypeOf(this, UploadClientError.prototype);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
413
|
const REQUEST_WAS_THROTTLED_CODE = 'RequestThrottledError';
|
|
438
414
|
const DEFAULT_RETRY_AFTER_TIMEOUT = 15000;
|
|
439
415
|
const DEFAULT_NETWORK_ERROR_TIMEOUT = 1000;
|
|
@@ -464,6 +440,31 @@ function retryIfFailed(fn, options) {
|
|
|
464
440
|
}));
|
|
465
441
|
}
|
|
466
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
|
+
|
|
467
468
|
function getStoreValue(store) {
|
|
468
469
|
return typeof store === 'undefined' ? 'auto' : store ? '1' : '0';
|
|
469
470
|
}
|
|
@@ -479,13 +480,13 @@ function base(file, { publicKey, fileName, contentType, baseURL = defaultSetting
|
|
|
479
480
|
jsonerrors: 1
|
|
480
481
|
}),
|
|
481
482
|
headers: {
|
|
482
|
-
'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
|
|
483
|
+
'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
|
|
483
484
|
},
|
|
484
485
|
data: buildFormData({
|
|
485
486
|
file: {
|
|
486
487
|
data: file,
|
|
487
|
-
name: fileName
|
|
488
|
-
contentType
|
|
488
|
+
name: fileName || getFilename(file),
|
|
489
|
+
contentType: contentType || getContentType(file)
|
|
489
490
|
},
|
|
490
491
|
UPLOADCARE_PUB_KEY: publicKey,
|
|
491
492
|
UPLOADCARE_STORE: getStoreValue(store),
|
|
@@ -683,9 +684,9 @@ function multipartStart(size, { publicKey, contentType, fileName, multipartChunk
|
|
|
683
684
|
'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
|
|
684
685
|
},
|
|
685
686
|
data: buildFormData({
|
|
686
|
-
filename: fileName
|
|
687
|
+
filename: fileName || defaultFilename,
|
|
687
688
|
size: size,
|
|
688
|
-
content_type: contentType
|
|
689
|
+
content_type: contentType || defaultContentType,
|
|
689
690
|
part_size: multipartChunkSize,
|
|
690
691
|
UPLOADCARE_STORE: getStoreValue(store),
|
|
691
692
|
UPLOADCARE_PUB_KEY: publicKey,
|
|
@@ -762,6 +763,28 @@ function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL,
|
|
|
762
763
|
}), { retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes });
|
|
763
764
|
}
|
|
764
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
|
+
|
|
765
788
|
class UploadcareFile {
|
|
766
789
|
constructor(fileInfo, { baseCDN, fileName }) {
|
|
767
790
|
this.name = null;
|
|
@@ -799,28 +822,6 @@ class UploadcareFile {
|
|
|
799
822
|
}
|
|
800
823
|
}
|
|
801
824
|
|
|
802
|
-
function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes, signal, onProgress }) {
|
|
803
|
-
return poll({
|
|
804
|
-
check: (signal) => info(file, {
|
|
805
|
-
publicKey,
|
|
806
|
-
baseURL,
|
|
807
|
-
signal,
|
|
808
|
-
source,
|
|
809
|
-
integration,
|
|
810
|
-
userAgent,
|
|
811
|
-
retryThrottledRequestMaxTimes,
|
|
812
|
-
retryNetworkErrorMaxTimes
|
|
813
|
-
}).then((response) => {
|
|
814
|
-
if (response.isReady) {
|
|
815
|
-
return response;
|
|
816
|
-
}
|
|
817
|
-
onProgress && onProgress({ isComputable: true, value: 1 });
|
|
818
|
-
return false;
|
|
819
|
-
}),
|
|
820
|
-
signal
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
|
|
824
825
|
const uploadDirect = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, contentType, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes, baseCDN, metadata }) => {
|
|
825
826
|
return base(file, {
|
|
826
827
|
publicKey,
|
|
@@ -856,6 +857,29 @@ const uploadDirect = (file, { publicKey, fileName, baseURL, secureSignature, sec
|
|
|
856
857
|
.then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
|
|
857
858
|
};
|
|
858
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
|
+
|
|
859
883
|
const race = (fns, { signal } = {}) => {
|
|
860
884
|
let lastError = null;
|
|
861
885
|
let winnerIndex = null;
|
|
@@ -1195,35 +1219,30 @@ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, check
|
|
|
1195
1219
|
}))
|
|
1196
1220
|
.then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
|
|
1197
1221
|
|
|
1198
|
-
const
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
retryThrottledRequestMaxTimes,
|
|
1207
|
-
retryNetworkErrorMaxTimes
|
|
1208
|
-
})
|
|
1209
|
-
.then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN, fileName }))
|
|
1210
|
-
.then((result) => {
|
|
1211
|
-
// hack for node ¯\_(ツ)_/¯
|
|
1212
|
-
if (onProgress)
|
|
1213
|
-
onProgress({
|
|
1214
|
-
isComputable: true,
|
|
1215
|
-
value: 1
|
|
1216
|
-
});
|
|
1217
|
-
return result;
|
|
1218
|
-
});
|
|
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;
|
|
1219
1230
|
};
|
|
1220
1231
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
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}`);
|
|
1226
1244
|
};
|
|
1245
|
+
|
|
1227
1246
|
/**
|
|
1228
1247
|
* Check if FileData is multipart data.
|
|
1229
1248
|
*/
|
|
@@ -1231,28 +1250,24 @@ const isMultipart = (fileSize, multipartMinFileSize = defaultSettings.multipartM
|
|
|
1231
1250
|
return fileSize >= multipartMinFileSize;
|
|
1232
1251
|
};
|
|
1233
1252
|
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
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);
|
|
1238
1260
|
};
|
|
1239
|
-
|
|
1240
1261
|
/**
|
|
1241
|
-
*
|
|
1242
|
-
*
|
|
1243
|
-
* We need to store references to sliced blobs to prevent source blob from
|
|
1244
|
-
* being deallocated until uploading complete. Access to deallocated blob
|
|
1245
|
-
* causes app crash.
|
|
1262
|
+
* Url type guard.
|
|
1246
1263
|
*
|
|
1247
|
-
*
|
|
1248
|
-
* and https://github.com/facebook/react-native/issues/27543
|
|
1264
|
+
* @param {AnyFile | Url | Uuid} data
|
|
1249
1265
|
*/
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
}
|
|
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
|
+
};
|
|
1256
1271
|
|
|
1257
1272
|
const runWithConcurrency = (concurrency, tasks) => {
|
|
1258
1273
|
return new Promise((resolve, reject) => {
|
|
@@ -1289,6 +1304,36 @@ const runWithConcurrency = (concurrency, tasks) => {
|
|
|
1289
1304
|
});
|
|
1290
1305
|
};
|
|
1291
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
|
+
|
|
1292
1337
|
const uploadPart = (chunk, url, { publicKey, onProgress, signal, integration, retryThrottledRequestMaxTimes, retryNetworkErrorMaxTimes }) => multipartUpload(chunk, url, {
|
|
1293
1338
|
publicKey,
|
|
1294
1339
|
onProgress,
|
|
@@ -1297,8 +1342,8 @@ const uploadPart = (chunk, url, { publicKey, onProgress, signal, integration, re
|
|
|
1297
1342
|
retryThrottledRequestMaxTimes,
|
|
1298
1343
|
retryNetworkErrorMaxTimes
|
|
1299
1344
|
});
|
|
1300
|
-
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 }) => {
|
|
1301
|
-
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);
|
|
1302
1347
|
let progressValues;
|
|
1303
1348
|
const createProgressHandler = (totalChunks, chunkIdx) => {
|
|
1304
1349
|
if (!onProgress)
|
|
@@ -1320,8 +1365,8 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
|
|
|
1320
1365
|
};
|
|
1321
1366
|
return multipartStart(size, {
|
|
1322
1367
|
publicKey,
|
|
1323
|
-
contentType,
|
|
1324
|
-
fileName: fileName
|
|
1368
|
+
contentType: contentType || getContentType(file),
|
|
1369
|
+
fileName: fileName || getFilename(file),
|
|
1325
1370
|
baseURL,
|
|
1326
1371
|
secureSignature,
|
|
1327
1372
|
secureExpire,
|
|
@@ -1334,8 +1379,8 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
|
|
|
1334
1379
|
retryNetworkErrorMaxTimes,
|
|
1335
1380
|
metadata
|
|
1336
1381
|
})
|
|
1337
|
-
.then(({ uuid, parts }) => {
|
|
1338
|
-
const getChunk = prepareChunks(file, size, multipartChunkSize);
|
|
1382
|
+
.then(async ({ uuid, parts }) => {
|
|
1383
|
+
const getChunk = await prepareChunks(file, size, multipartChunkSize);
|
|
1339
1384
|
return Promise.all([
|
|
1340
1385
|
uuid,
|
|
1341
1386
|
runWithConcurrency(maxConcurrentRequests, parts.map((url, index) => () => uploadPart(getChunk(index), url, {
|
|
@@ -1382,14 +1427,15 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
|
|
|
1382
1427
|
/**
|
|
1383
1428
|
* Uploads file from provided data.
|
|
1384
1429
|
*/
|
|
1385
|
-
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 }) {
|
|
1386
1431
|
if (isFileData(data)) {
|
|
1387
|
-
const fileSize = getFileSize(data);
|
|
1432
|
+
const fileSize = await getFileSize(data);
|
|
1388
1433
|
if (isMultipart(fileSize, multipartMinFileSize)) {
|
|
1389
1434
|
return uploadMultipart(data, {
|
|
1390
1435
|
publicKey,
|
|
1391
1436
|
contentType,
|
|
1392
1437
|
multipartChunkSize,
|
|
1438
|
+
fileSize,
|
|
1393
1439
|
fileName,
|
|
1394
1440
|
baseURL,
|
|
1395
1441
|
secureSignature,
|