@or-sdk/files 3.7.3 → 3.8.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/CHANGELOG.md +9 -0
- package/dist/cjs/Files.js +190 -113
- package/dist/cjs/Files.js.map +1 -1
- package/dist/cjs/utils.js +7 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/esm/Files.js +126 -79
- package/dist/esm/Files.js.map +1 -1
- package/dist/esm/utils.js +4 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/types/Files.d.ts +7 -10
- package/dist/types/Files.d.ts.map +1 -1
- package/dist/types/types.d.ts +20 -10
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils.d.ts +2 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/package.json +7 -4
- package/src/Files.ts +148 -104
- package/src/types.ts +25 -10
- package/src/utils.ts +3 -0
- package/tsconfig.esm.json +1 -1
package/src/Files.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Base } from '@or-sdk/base';
|
|
2
2
|
import axios from 'axios';
|
|
3
|
-
import type
|
|
4
|
-
import {
|
|
3
|
+
import type FormDataNode from 'form-data';
|
|
4
|
+
import { Memoize } from 'typescript-memoize';
|
|
5
5
|
|
|
6
6
|
import { SERVICE_KEY } from './constants';
|
|
7
|
-
import {
|
|
7
|
+
import type {
|
|
8
8
|
FileItem,
|
|
9
9
|
FileItemSelect,
|
|
10
10
|
FilesConfig,
|
|
@@ -13,8 +13,11 @@ import {
|
|
|
13
13
|
UploadFileProps,
|
|
14
14
|
UploadSystemFileParams,
|
|
15
15
|
UploadSystemUrlResponse,
|
|
16
|
+
UploadToSignedUrlParameters,
|
|
17
|
+
UploadUrlProps,
|
|
16
18
|
UploadUrlResponse,
|
|
17
19
|
} from './types';
|
|
20
|
+
import { isNode } from './utils';
|
|
18
21
|
|
|
19
22
|
export class Files extends Base {
|
|
20
23
|
/**
|
|
@@ -72,7 +75,7 @@ export class Files extends Base {
|
|
|
72
75
|
* @return total folder size in bytes
|
|
73
76
|
*/
|
|
74
77
|
async getFolderSize(key: string): Promise<number> {
|
|
75
|
-
const { size } = await this.callApiV2<{size: number;}>({
|
|
78
|
+
const { size } = await this.callApiV2<{ size: number; }>({
|
|
76
79
|
method: 'get',
|
|
77
80
|
route: 'folders/find-one',
|
|
78
81
|
params: { key },
|
|
@@ -104,34 +107,19 @@ export class Files extends Base {
|
|
|
104
107
|
attributes,
|
|
105
108
|
};
|
|
106
109
|
|
|
107
|
-
|
|
110
|
+
const [files, folders] = await Promise.all([
|
|
111
|
+
this.callApiV2<FileItem[] | FileItemSelect[]>({
|
|
112
|
+
method: 'GET',
|
|
113
|
+
route: 'files',
|
|
114
|
+
params: queryParams,
|
|
115
|
+
}),
|
|
116
|
+
isPublic ? [] as FileItem[] : this.callApiV2<FileItem[]>({
|
|
117
|
+
method: 'GET',
|
|
118
|
+
route: 'folders',
|
|
119
|
+
params: { prefix: treePrefix },
|
|
120
|
+
}),
|
|
121
|
+
]);
|
|
108
122
|
|
|
109
|
-
if (isPublic) {
|
|
110
|
-
res = await Promise.all([
|
|
111
|
-
this.callApiV2<FileItem[] | FileItemSelect[]>({
|
|
112
|
-
method: 'GET',
|
|
113
|
-
route: 'files',
|
|
114
|
-
params: queryParams,
|
|
115
|
-
}),
|
|
116
|
-
[], // we prevent unnecessary request to get public folders, because they only private
|
|
117
|
-
]);
|
|
118
|
-
} else {
|
|
119
|
-
res = await Promise.all([
|
|
120
|
-
this.callApiV2<FileItem[] | FileItemSelect[]>({
|
|
121
|
-
method: 'GET',
|
|
122
|
-
route: 'files',
|
|
123
|
-
params: queryParams,
|
|
124
|
-
}),
|
|
125
|
-
this.callApiV2<FileItem[]>({
|
|
126
|
-
method: 'GET',
|
|
127
|
-
route: 'folders',
|
|
128
|
-
params: { prefix: treePrefix },
|
|
129
|
-
}),
|
|
130
|
-
]);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const [files, folders] = res;
|
|
135
123
|
return [...folders, ...files];
|
|
136
124
|
}
|
|
137
125
|
|
|
@@ -155,17 +143,21 @@ export class Files extends Base {
|
|
|
155
143
|
prefix: term,
|
|
156
144
|
};
|
|
157
145
|
|
|
158
|
-
const files
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
146
|
+
const [files, folders] = await Promise.all([
|
|
147
|
+
this.callApiV2<FileItem[]>({
|
|
148
|
+
method: 'GET',
|
|
149
|
+
route: 'files/search',
|
|
150
|
+
params: queryParams,
|
|
151
|
+
}),
|
|
163
152
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
153
|
+
isPublic
|
|
154
|
+
? [] as FileItem[]
|
|
155
|
+
: this.callApiV2<FileItem[]>({
|
|
156
|
+
method: 'GET',
|
|
157
|
+
route: 'folders/search',
|
|
158
|
+
params: queryParams,
|
|
159
|
+
}),
|
|
160
|
+
]);
|
|
169
161
|
|
|
170
162
|
return [...files, ...folders];
|
|
171
163
|
}
|
|
@@ -233,7 +225,7 @@ export class Files extends Base {
|
|
|
233
225
|
const STEP = 2000; // 2 second in ms
|
|
234
226
|
let passed = 0;
|
|
235
227
|
|
|
236
|
-
|
|
228
|
+
await new Promise((res, rej) => {
|
|
237
229
|
// it may take some time to copy file on S3, so we check it each "STEP" time
|
|
238
230
|
const interval = setInterval(() => {
|
|
239
231
|
this.getFile(newKey, isPublic)
|
|
@@ -245,8 +237,6 @@ export class Files extends Base {
|
|
|
245
237
|
.catch(() => passed += STEP);
|
|
246
238
|
}, STEP);
|
|
247
239
|
});
|
|
248
|
-
|
|
249
|
-
await promise;
|
|
250
240
|
}
|
|
251
241
|
|
|
252
242
|
/**
|
|
@@ -325,9 +315,12 @@ export class Files extends Base {
|
|
|
325
315
|
* @param ttl timestamp of file expiration
|
|
326
316
|
* @return uploading Url with different header Fields
|
|
327
317
|
*/
|
|
328
|
-
getUploadUrl(
|
|
329
|
-
|
|
330
|
-
|
|
318
|
+
getUploadUrl(
|
|
319
|
+
data: UploadUrlProps,
|
|
320
|
+
isPublic: boolean,
|
|
321
|
+
ttl?: number,
|
|
322
|
+
abortSignal?: AbortSignal,
|
|
323
|
+
): Promise<UploadUrlResponse> {
|
|
331
324
|
const reqData: GetUploadUrlDataPayload = { ...data };
|
|
332
325
|
if (ttl) reqData.ttl = new Date(ttl).toISOString();
|
|
333
326
|
|
|
@@ -358,7 +351,6 @@ export class Files extends Base {
|
|
|
358
351
|
* @param waitTillFileAddedInDb true if you want to make sure file added to DB,
|
|
359
352
|
* for example for instant removing it. Please, use it carefully.
|
|
360
353
|
*/
|
|
361
|
-
// eslint-disable-next-line max-len
|
|
362
354
|
async uploadFile({
|
|
363
355
|
type,
|
|
364
356
|
name,
|
|
@@ -369,32 +361,35 @@ export class Files extends Base {
|
|
|
369
361
|
rewriteMode,
|
|
370
362
|
ttl,
|
|
371
363
|
maxFileSize,
|
|
364
|
+
knownLength,
|
|
365
|
+
cacheControl = 'no-cache',
|
|
372
366
|
waitTillFileAddedInDb,
|
|
373
367
|
}: UploadFileProps, abortSignal?: AbortSignal): Promise<string> {
|
|
374
368
|
const contentType = type || 'binary/octet-stream';
|
|
375
369
|
const fileKey = prefix + name;
|
|
376
370
|
|
|
377
|
-
const
|
|
371
|
+
const signedUrl = await this.getUploadUrl(
|
|
372
|
+
{
|
|
373
|
+
contentType,
|
|
374
|
+
key: fileKey,
|
|
375
|
+
cacheControl,
|
|
376
|
+
rewriteMode: rewriteMode,
|
|
377
|
+
maxFileSize: maxFileSize,
|
|
378
|
+
},
|
|
379
|
+
isPublic,
|
|
380
|
+
ttl,
|
|
381
|
+
abortSignal,
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
await this.uploadToSignedUrl({
|
|
385
|
+
signedUrl,
|
|
386
|
+
file: fileModel,
|
|
387
|
+
fileName: name,
|
|
388
|
+
cacheControl,
|
|
378
389
|
contentType,
|
|
379
|
-
|
|
380
|
-
cacheControl: 'no-cache',
|
|
381
|
-
rewriteMode: rewriteMode,
|
|
382
|
-
maxFileSize: maxFileSize,
|
|
383
|
-
}, isPublic, ttl, abortSignal);
|
|
384
|
-
|
|
385
|
-
const FormDataLib = isNode ? require('form-data') : FormData; // eslint-disable-line no-undef
|
|
386
|
-
const formData = new FormDataLib();
|
|
387
|
-
|
|
388
|
-
Object.entries(fields).forEach(value => {
|
|
389
|
-
formData.append(value[0], value[1]);
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
formData.append('content-type', contentType);
|
|
393
|
-
formData.append('File', fileModel, name);
|
|
394
|
-
|
|
395
|
-
await axios.post(url, formData, {
|
|
390
|
+
knownLength,
|
|
396
391
|
signal: abortSignal,
|
|
397
|
-
onUploadProgress:
|
|
392
|
+
onUploadProgress: progress,
|
|
398
393
|
});
|
|
399
394
|
|
|
400
395
|
if (waitTillFileAddedInDb) {
|
|
@@ -402,7 +397,7 @@ export class Files extends Base {
|
|
|
402
397
|
const STEP = 2000; // 2 second in ms
|
|
403
398
|
let passed = 0;
|
|
404
399
|
|
|
405
|
-
|
|
400
|
+
await new Promise((res, rej) => {
|
|
406
401
|
// it may take some time to copy file on S3, so we check it each "STEP" time
|
|
407
402
|
const interval = setInterval(() => {
|
|
408
403
|
this.getFile(fileKey, isPublic)
|
|
@@ -414,11 +409,9 @@ export class Files extends Base {
|
|
|
414
409
|
.catch(() => passed += STEP);
|
|
415
410
|
}, STEP);
|
|
416
411
|
});
|
|
417
|
-
|
|
418
|
-
await promise;
|
|
419
412
|
}
|
|
420
413
|
|
|
421
|
-
return downloadUrl;
|
|
414
|
+
return signedUrl.downloadUrl;
|
|
422
415
|
}
|
|
423
416
|
|
|
424
417
|
// ------------------------
|
|
@@ -431,14 +424,15 @@ export class Files extends Base {
|
|
|
431
424
|
* @param file file for uploading
|
|
432
425
|
* @param cacheControl cache settings
|
|
433
426
|
* @param abortSignal signal to cancel uploading
|
|
427
|
+
* @deprecated use `uploadSystemFileV2` instead
|
|
434
428
|
*/
|
|
435
429
|
async uploadSystemFile(
|
|
436
430
|
prefix: string,
|
|
437
431
|
file: File,
|
|
438
432
|
cacheControl = 'max-age=3600',
|
|
439
|
-
abortSignal?: AbortSignal
|
|
433
|
+
abortSignal?: AbortSignal,
|
|
440
434
|
): Promise<string> {
|
|
441
|
-
const
|
|
435
|
+
const signedUrl: UploadSystemUrlResponse = await this.callApiV2({
|
|
442
436
|
method: 'post',
|
|
443
437
|
route: 'system-file',
|
|
444
438
|
data: {
|
|
@@ -449,24 +443,16 @@ export class Files extends Base {
|
|
|
449
443
|
signal: abortSignal,
|
|
450
444
|
});
|
|
451
445
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
formData.append(value[0], value[1]);
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
formData.append('cache-control', cacheControl);
|
|
462
|
-
formData.append('content-type', file.type);
|
|
463
|
-
formData.append('File', file, file.name);
|
|
464
|
-
|
|
465
|
-
await axios.post(url, formData, {
|
|
446
|
+
await this.uploadToSignedUrl({
|
|
447
|
+
signedUrl,
|
|
448
|
+
file,
|
|
449
|
+
fileName: file.name,
|
|
450
|
+
cacheControl,
|
|
451
|
+
contentType: file.type,
|
|
466
452
|
signal: abortSignal,
|
|
467
453
|
});
|
|
468
454
|
|
|
469
|
-
return downloadUrl;
|
|
455
|
+
return signedUrl.downloadUrl;
|
|
470
456
|
}
|
|
471
457
|
|
|
472
458
|
/**
|
|
@@ -487,9 +473,10 @@ export class Files extends Base {
|
|
|
487
473
|
ttl,
|
|
488
474
|
fileName,
|
|
489
475
|
contentType,
|
|
476
|
+
knownLength,
|
|
490
477
|
abortSignal,
|
|
491
478
|
}: UploadSystemFileParams): Promise<string> {
|
|
492
|
-
const
|
|
479
|
+
const signedUrl: UploadSystemUrlResponse = await this.callApiV2({
|
|
493
480
|
method: 'post',
|
|
494
481
|
route: 'system-file',
|
|
495
482
|
data: {
|
|
@@ -501,24 +488,17 @@ export class Files extends Base {
|
|
|
501
488
|
signal: abortSignal,
|
|
502
489
|
});
|
|
503
490
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
formData.append('cache-control', cacheControl);
|
|
514
|
-
formData.append('content-type', contentType);
|
|
515
|
-
formData.append('File', file, fileName);
|
|
516
|
-
|
|
517
|
-
await axios.post(url, formData, {
|
|
491
|
+
await this.uploadToSignedUrl({
|
|
492
|
+
signedUrl,
|
|
493
|
+
file,
|
|
494
|
+
fileName,
|
|
495
|
+
cacheControl,
|
|
496
|
+
contentType,
|
|
497
|
+
knownLength,
|
|
518
498
|
signal: abortSignal,
|
|
519
499
|
});
|
|
520
500
|
|
|
521
|
-
return downloadUrl;
|
|
501
|
+
return signedUrl.downloadUrl;
|
|
522
502
|
}
|
|
523
503
|
|
|
524
504
|
/**
|
|
@@ -584,4 +564,68 @@ export class Files extends Base {
|
|
|
584
564
|
params: { key: path },
|
|
585
565
|
});
|
|
586
566
|
}
|
|
567
|
+
|
|
568
|
+
private async uploadToSignedUrl({
|
|
569
|
+
signedUrl,
|
|
570
|
+
file,
|
|
571
|
+
fileName,
|
|
572
|
+
cacheControl,
|
|
573
|
+
contentType,
|
|
574
|
+
knownLength,
|
|
575
|
+
signal,
|
|
576
|
+
onUploadProgress,
|
|
577
|
+
}: UploadToSignedUrlParameters) {
|
|
578
|
+
const { url, fields } = signedUrl;
|
|
579
|
+
|
|
580
|
+
const FormDataLib = await this.formDataFactory();
|
|
581
|
+
const formData = new FormDataLib();
|
|
582
|
+
|
|
583
|
+
Object.entries(fields).forEach(([key, value]) => {
|
|
584
|
+
formData.append(key, value);
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
if (cacheControl != undefined && cacheControl !== 'no-cache') {
|
|
588
|
+
formData.append('cache-control', cacheControl);
|
|
589
|
+
}
|
|
590
|
+
if (contentType) formData.append('content-type', contentType);
|
|
591
|
+
|
|
592
|
+
if (isNode) {
|
|
593
|
+
(formData as FormDataNode).append('File', file, {
|
|
594
|
+
filename: fileName,
|
|
595
|
+
contentType,
|
|
596
|
+
knownLength,
|
|
597
|
+
});
|
|
598
|
+
} else {
|
|
599
|
+
if (!(file instanceof File || file instanceof Blob)) {
|
|
600
|
+
throw new Error('In browser file can only be an instance of File or Blob');
|
|
601
|
+
}
|
|
602
|
+
(formData as FormData).append('File', file, fileName);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
return await axios.post(url, formData, {
|
|
606
|
+
headers: {
|
|
607
|
+
...this.getFormDataHeaders(formData),
|
|
608
|
+
},
|
|
609
|
+
onUploadProgress,
|
|
610
|
+
maxBodyLength: Infinity,
|
|
611
|
+
maxContentLength: Infinity,
|
|
612
|
+
signal,
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
@Memoize()
|
|
617
|
+
private async formDataFactory(): Promise<typeof FormDataNode | typeof FormData> {
|
|
618
|
+
if (isNode) {
|
|
619
|
+
const formDataModule = await import('form-data');
|
|
620
|
+
return formDataModule.default;
|
|
621
|
+
} else {
|
|
622
|
+
return FormData;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
private getFormDataHeaders(formData: FormDataNode | FormData): Record<string, string> {
|
|
627
|
+
return ('getHeaders' in formData)
|
|
628
|
+
? formData.getHeaders()
|
|
629
|
+
: {};
|
|
630
|
+
}
|
|
587
631
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Token } from '@or-sdk/base';
|
|
1
|
+
import type { Token } from '@or-sdk/base';
|
|
2
2
|
import type { AxiosProgressEvent } from 'axios';
|
|
3
|
+
import type { ReadStream } from 'node:fs';
|
|
3
4
|
|
|
4
5
|
export type FilesConfig = {
|
|
5
6
|
/**
|
|
@@ -69,7 +70,7 @@ export type UploadUrlProps = {
|
|
|
69
70
|
key: string;
|
|
70
71
|
contentType: string;
|
|
71
72
|
maxFileSize?: number;
|
|
72
|
-
cacheControl
|
|
73
|
+
cacheControl?: string;
|
|
73
74
|
rewriteMode?: 'rewrite' | 'prevent-rewrite';
|
|
74
75
|
};
|
|
75
76
|
|
|
@@ -85,12 +86,7 @@ export type UploadFields = {
|
|
|
85
86
|
'cache-control'?: string;
|
|
86
87
|
};
|
|
87
88
|
|
|
88
|
-
export type GetUploadUrlDataPayload = {
|
|
89
|
-
rewriteMode?: 'rewrite' | 'prevent-rewrite';
|
|
90
|
-
maxFileSize?: number;
|
|
91
|
-
contentType: string;
|
|
92
|
-
key: string;
|
|
93
|
-
cacheControl: string;
|
|
89
|
+
export type GetUploadUrlDataPayload = UploadUrlProps & {
|
|
94
90
|
ttl?: string;
|
|
95
91
|
};
|
|
96
92
|
|
|
@@ -106,26 +102,45 @@ export type UploadSystemUrlResponse = {
|
|
|
106
102
|
downloadUrl: string;
|
|
107
103
|
};
|
|
108
104
|
|
|
105
|
+
/** Buffer and ReadStream only supported in Node.js */
|
|
106
|
+
export type FileModel = File | Blob | Buffer | ReadStream
|
|
107
|
+
|
|
109
108
|
export type UploadFileProps = {
|
|
109
|
+
/** mime type of the file */
|
|
110
110
|
type: string;
|
|
111
111
|
name: string;
|
|
112
|
-
fileModel:
|
|
112
|
+
fileModel: FileModel;
|
|
113
113
|
prefix: string;
|
|
114
114
|
isPublic: boolean;
|
|
115
115
|
progress?: (event: ProgressEvent | AxiosProgressEvent) => void;
|
|
116
116
|
rewriteMode?: 'rewrite' | 'prevent-rewrite';
|
|
117
117
|
ttl?: number;
|
|
118
118
|
maxFileSize?: number;
|
|
119
|
+
knownLength?: number;
|
|
120
|
+
cacheControl?: string;
|
|
119
121
|
abortSignal?: AbortSignal;
|
|
120
122
|
waitTillFileAddedInDb?: boolean;
|
|
121
123
|
};
|
|
122
124
|
|
|
123
125
|
export type UploadSystemFileParams = {
|
|
124
126
|
prefix: string;
|
|
125
|
-
file:
|
|
127
|
+
file: FileModel;
|
|
126
128
|
cacheControl?: string;
|
|
127
129
|
ttl: number;
|
|
128
130
|
fileName: string;
|
|
129
131
|
contentType?: string;
|
|
132
|
+
knownLength?: number;
|
|
130
133
|
abortSignal?: AbortSignal;
|
|
131
134
|
};
|
|
135
|
+
|
|
136
|
+
export type UploadToSignedUrlParameters = {
|
|
137
|
+
signedUrl: UploadUrlResponse;
|
|
138
|
+
file: FileModel;
|
|
139
|
+
fileName: string;
|
|
140
|
+
cacheControl?: string;
|
|
141
|
+
/** Size of the file in bytes. Helpful when file is an instance of ReadStream in Node.js */
|
|
142
|
+
knownLength?: number;
|
|
143
|
+
contentType?: string;
|
|
144
|
+
signal?: AbortSignal;
|
|
145
|
+
onUploadProgress?: (event: ProgressEvent | AxiosProgressEvent) => void;
|
|
146
|
+
}
|
package/src/utils.ts
ADDED