dpu-cloud-sdk 1.0.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/.env.development +1 -0
- package/.env.production +1 -0
- package/dist/DPUClient.d.ts +83 -0
- package/dist/DPUClient.js +1043 -0
- package/dist/ServiceIntegration.d.ts +20 -0
- package/dist/ServiceIntegration.js +506 -0
- package/dist/api/auth.d.ts +3 -0
- package/dist/api/auth.js +10 -0
- package/dist/api/compress.d.ts +4 -0
- package/dist/api/compress.js +16 -0
- package/dist/api/translate.d.ts +8 -0
- package/dist/api/translate.js +38 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/models/RequestModel.d.ts +33 -0
- package/dist/models/RequestModel.js +2 -0
- package/dist/models/ResponseModel.d.ts +99 -0
- package/dist/models/ResponseModel.js +1 -0
- package/dist/utils/Config.d.ts +32 -0
- package/dist/utils/Config.js +44 -0
- package/dist/utils/Constants.d.ts +48 -0
- package/dist/utils/Constants.js +55 -0
- package/dist/utils/Enum.d.ts +27 -0
- package/dist/utils/Enum.js +30 -0
- package/dist/utils/Helper.d.ts +4 -0
- package/dist/utils/Helper.js +47 -0
- package/dist/workerDownloadSingleFile.d.ts +1 -0
- package/dist/workerDownloadSingleFile.js +35 -0
- package/dist/workerUploadChildFile.d.ts +1 -0
- package/dist/workerUploadChildFile.js +82 -0
- package/dist/workerUploadSingleFile.d.ts +1 -0
- package/dist/workerUploadSingleFile.js +93 -0
- package/dpubim-service-1.1.28.tgz +0 -0
- package/package.json +33 -0
- package/src/DPUClient.ts +1505 -0
- package/src/ServiceIntegration.ts +710 -0
- package/src/api/auth.ts +18 -0
- package/src/api/compress.ts +36 -0
- package/src/api/translate.ts +94 -0
- package/src/index.ts +4 -0
- package/src/models/RequestModel.ts +44 -0
- package/src/models/ResponseModel.ts +110 -0
- package/src/utils/Config.ts +59 -0
- package/src/utils/Constants.ts +61 -0
- package/src/utils/Enum.ts +29 -0
- package/src/utils/Helper.ts +57 -0
- package/src/workerDownloadSingleFile.ts +34 -0
- package/src/workerUploadChildFile.ts +85 -0
- package/src/workerUploadSingleFile.ts +123 -0
- package/tsconfig.json +108 -0
- package/webpack.config.js +43 -0
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
import { FileDownloadInfo } from "./models/RequestModel";
|
|
2
|
+
import {
|
|
3
|
+
BaseReponseModel,
|
|
4
|
+
InitUploadResponse,
|
|
5
|
+
PresignURLResponse,
|
|
6
|
+
ObjectDetail,
|
|
7
|
+
CompleteMultipartUploadResponse,
|
|
8
|
+
MultiPresignURLResponse,
|
|
9
|
+
} from "./models/ResponseModel";
|
|
10
|
+
import { ApiStatus, ConfigFileRules, Path } from "./utils/Constants";
|
|
11
|
+
import { validString } from "./utils/Helper";
|
|
12
|
+
|
|
13
|
+
export class ServiceIntegration {
|
|
14
|
+
constructor() {}
|
|
15
|
+
|
|
16
|
+
public async getUrlToDownload(
|
|
17
|
+
bucketName: string,
|
|
18
|
+
fileName: string,
|
|
19
|
+
accessToken?: string
|
|
20
|
+
) {
|
|
21
|
+
try {
|
|
22
|
+
const url = `${Path.BaseURL}${Path.GetUrlDownload}`;
|
|
23
|
+
const headers: any = {
|
|
24
|
+
Authorization: `Bearer ${accessToken}`,
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
"ngrok-skip-browser-warning": true,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let queryParams: any;
|
|
30
|
+
if (validString(bucketName)) {
|
|
31
|
+
queryParams = { ...queryParams, bucketName: bucketName };
|
|
32
|
+
}
|
|
33
|
+
if (validString(fileName)) {
|
|
34
|
+
queryParams = { ...queryParams, fileName: fileName };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const response = await fetch(
|
|
38
|
+
`${url}?${new URLSearchParams(queryParams)}`,
|
|
39
|
+
{
|
|
40
|
+
method: "GET",
|
|
41
|
+
headers: headers,
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
if (response.ok) {
|
|
45
|
+
const json: BaseReponseModel<string> =
|
|
46
|
+
(await response.json()) as BaseReponseModel<string>;
|
|
47
|
+
if (json.statusCode === ApiStatus.Success) {
|
|
48
|
+
return json.data;
|
|
49
|
+
} else {
|
|
50
|
+
console.error(
|
|
51
|
+
`Fail to generate url download with message error: ${json.message}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
console.error(
|
|
56
|
+
`Fail to generate url download with status ${response.statusText} and error: ${response.statusText}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return null;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error(`Error when generate url download: ${error}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public async fetchChunkFile(url: string, start: number, end: number) {
|
|
67
|
+
try {
|
|
68
|
+
const response = await fetch(url, {
|
|
69
|
+
method: "GET",
|
|
70
|
+
headers: {
|
|
71
|
+
Range: `bytes=${start}-${end}`,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const error = `Fail to fetch chunk file with status ${
|
|
76
|
+
response.statusText
|
|
77
|
+
} and error: ${await response.text()}`;
|
|
78
|
+
throw new Error(error);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return await response.arrayBuffer();
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(`Error when download file: ${error}`);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//#region Upload small file
|
|
89
|
+
public async uploadSmallFile(
|
|
90
|
+
bucketName: string,
|
|
91
|
+
fileName: string,
|
|
92
|
+
buffer: Buffer,
|
|
93
|
+
// file: File,
|
|
94
|
+
accessToken: string,
|
|
95
|
+
cancellationToken: AbortController,
|
|
96
|
+
onProgress?: (percentCompleted: number) => void,
|
|
97
|
+
reGetAccessToken?: () => string,
|
|
98
|
+
isGetInfo?: boolean,
|
|
99
|
+
preSignUrl?: PresignURLResponse
|
|
100
|
+
) {
|
|
101
|
+
if(!preSignUrl) {
|
|
102
|
+
const presignUrlResponse = await this.generatePresignedUrl(
|
|
103
|
+
bucketName,
|
|
104
|
+
fileName,
|
|
105
|
+
accessToken,
|
|
106
|
+
cancellationToken
|
|
107
|
+
);
|
|
108
|
+
if (!presignUrlResponse) {
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
reject(`Fail to generate presigned url for file ${fileName}`);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
preSignUrl = presignUrlResponse;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
var responseUploadChild = await this.uploadChildFile(
|
|
117
|
+
preSignUrl,
|
|
118
|
+
buffer,
|
|
119
|
+
cancellationToken
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if (!responseUploadChild) {
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
reject(`Fail to upload file ${fileName}`);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (isGetInfo === true) {
|
|
129
|
+
const objectDetail: ObjectDetail | null = await this.getObjectDetail(
|
|
130
|
+
bucketName,
|
|
131
|
+
fileName,
|
|
132
|
+
accessToken
|
|
133
|
+
);
|
|
134
|
+
if (!objectDetail) {
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
reject(`Fail to get object detail for file ${fileName}`);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
const fileModel = {
|
|
140
|
+
bucketName: bucketName,
|
|
141
|
+
fileName: fileName,
|
|
142
|
+
contentLength: buffer.length,
|
|
143
|
+
versionFile: objectDetail.versionId,
|
|
144
|
+
dateVersionFile: objectDetail.lastModified,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
resolve(fileModel);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return new Promise((resolve, reject) => {
|
|
153
|
+
resolve({
|
|
154
|
+
bucketName: bucketName,
|
|
155
|
+
fileName: fileName,
|
|
156
|
+
contentLength: buffer.length,
|
|
157
|
+
versionFile: null,
|
|
158
|
+
dateVersionFile: null,
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private async generatePresignedUrl(
|
|
164
|
+
bucketName: string,
|
|
165
|
+
fileName: string,
|
|
166
|
+
accessToken?: string,
|
|
167
|
+
cancellationToken?: AbortController
|
|
168
|
+
) {
|
|
169
|
+
try {
|
|
170
|
+
const url = `${Path.BaseURL}${Path.GeneratePresignedUrl}`;
|
|
171
|
+
const headers: any = {
|
|
172
|
+
Authorization: `Bearer ${accessToken}`,
|
|
173
|
+
"Content-Type": "application/json",
|
|
174
|
+
"ngrok-skip-browser-warning": true,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
let queryParams: any = {
|
|
178
|
+
setPermission: false,
|
|
179
|
+
};
|
|
180
|
+
if (validString(bucketName)) {
|
|
181
|
+
queryParams = { ...queryParams, bucketName: bucketName };
|
|
182
|
+
}
|
|
183
|
+
if (validString(fileName)) {
|
|
184
|
+
queryParams = { ...queryParams, objectName: fileName };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const response = await fetch(
|
|
188
|
+
`${url}?${new URLSearchParams(queryParams)}`,
|
|
189
|
+
{
|
|
190
|
+
method: "GET",
|
|
191
|
+
headers: headers,
|
|
192
|
+
signal: cancellationToken?.signal
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
if (response.ok) {
|
|
196
|
+
const json: BaseReponseModel<PresignURLResponse> =
|
|
197
|
+
(await response.json()) as BaseReponseModel<PresignURLResponse>;
|
|
198
|
+
if (json.statusCode === ApiStatus.Success) {
|
|
199
|
+
return json.data;
|
|
200
|
+
} else {
|
|
201
|
+
console.error(
|
|
202
|
+
`Fail to generate presigned urls with message error: ${json.message}`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
console.error(
|
|
207
|
+
`Fail to generate presigned urls with status ${response.statusText} and error: ${response.statusText}`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return null;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error(`Error when generate presigned urls: ${error}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private async uploadChildFile(
|
|
218
|
+
presignUrlResponse: PresignURLResponse,
|
|
219
|
+
buffer: ArrayBuffer,
|
|
220
|
+
cancellationToken?: AbortController
|
|
221
|
+
) {
|
|
222
|
+
try {
|
|
223
|
+
const response = await fetch(presignUrlResponse.url, {
|
|
224
|
+
method: "PUT",
|
|
225
|
+
headers: presignUrlResponse.headers,
|
|
226
|
+
body: buffer,
|
|
227
|
+
signal: cancellationToken?.signal
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
if (response.status === ApiStatus.Success) {
|
|
231
|
+
var eTag: string | null = response.headers.get("ETag");
|
|
232
|
+
if (eTag) return eTag.replace(/^"|"$/g, "");
|
|
233
|
+
} else {
|
|
234
|
+
console.error(
|
|
235
|
+
`Fail to upload child file with status ${response.statusText} and error: ${response.statusText}`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return null;
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error(`Error when upload child file: ${error}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
|
|
246
|
+
//#region Upload large file
|
|
247
|
+
public async uploadLargeFile(
|
|
248
|
+
bucketName: string,
|
|
249
|
+
fileName: string,
|
|
250
|
+
buffer: Buffer,
|
|
251
|
+
accessToken: string,
|
|
252
|
+
cancellationToken: AbortController,
|
|
253
|
+
onProgress?: (percentCompleted: number) => void,
|
|
254
|
+
reGetAccessToken?: () => string,
|
|
255
|
+
initUpload?: InitUploadResponse,
|
|
256
|
+
isGetInfo?: boolean
|
|
257
|
+
) {
|
|
258
|
+
await this.validateFileSize(fileName, buffer.length);
|
|
259
|
+
// init multi upload
|
|
260
|
+
if (!initUpload) {
|
|
261
|
+
initUpload = await this.initMultiPartUpload(
|
|
262
|
+
bucketName,
|
|
263
|
+
fileName,
|
|
264
|
+
accessToken,
|
|
265
|
+
false,
|
|
266
|
+
cancellationToken
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
if (!initUpload) {
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
reject(`Fail to initiate multipart upload for file ${fileName}`);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
var numberOfChunks: number = this.calculateNumberOfChunks(
|
|
275
|
+
buffer.length
|
|
276
|
+
);
|
|
277
|
+
const urls = await this.generatePresignedUrls(
|
|
278
|
+
bucketName,
|
|
279
|
+
fileName,
|
|
280
|
+
initUpload,
|
|
281
|
+
numberOfChunks,
|
|
282
|
+
accessToken,
|
|
283
|
+
cancellationToken
|
|
284
|
+
);
|
|
285
|
+
if (!urls) {
|
|
286
|
+
return new Promise((resolve, reject) => {
|
|
287
|
+
reject(`Fail to generate presigned urls for file ${fileName}`);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
var chunksUploaded: number = 0;
|
|
292
|
+
var start: number = 0;
|
|
293
|
+
const eTags: any = {};
|
|
294
|
+
while (chunksUploaded < numberOfChunks) {
|
|
295
|
+
this.throwIfCancellationRequested(cancellationToken, fileName);
|
|
296
|
+
var end: number = Math.min(
|
|
297
|
+
start + ConfigFileRules.ChunkSize,
|
|
298
|
+
buffer.length
|
|
299
|
+
);
|
|
300
|
+
// var fileChunk: ArrayBuffer = await this.readFileBytes(file, start, end);
|
|
301
|
+
const sliced = buffer.subarray(start, end); // lấy phần con của buffer
|
|
302
|
+
const fileChunk = sliced.buffer.slice(sliced.byteOffset, sliced.byteOffset + sliced.byteLength);
|
|
303
|
+
this.throwIfCancellationRequested(cancellationToken, fileName);
|
|
304
|
+
var url: string = urls[chunksUploaded];
|
|
305
|
+
var responseUploadChild = await this.uploadChildFile(
|
|
306
|
+
{ url: url, headers: initUpload.headers },
|
|
307
|
+
fileChunk
|
|
308
|
+
);
|
|
309
|
+
if (!responseUploadChild) {
|
|
310
|
+
return new Promise((resolve, reject) => {
|
|
311
|
+
reject(`Fail to upload chunk ${chunksUploaded} of file ${fileName}`);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
Object.defineProperty(eTags, chunksUploaded + 1, {
|
|
315
|
+
value: responseUploadChild,
|
|
316
|
+
enumerable: true,
|
|
317
|
+
});
|
|
318
|
+
chunksUploaded++;
|
|
319
|
+
start = end;
|
|
320
|
+
var percentCompleted: number = (chunksUploaded / numberOfChunks) * 100;
|
|
321
|
+
onProgress?.(percentCompleted);
|
|
322
|
+
// console.log(`${requestId} Number of chunks uploaded : ${chunksUploaded}`);
|
|
323
|
+
}
|
|
324
|
+
var completeResponse = await this.completeMultipartUpload(
|
|
325
|
+
bucketName,
|
|
326
|
+
fileName,
|
|
327
|
+
initUpload.uploadId,
|
|
328
|
+
eTags,
|
|
329
|
+
accessToken,
|
|
330
|
+
cancellationToken
|
|
331
|
+
);
|
|
332
|
+
onProgress?.(100);
|
|
333
|
+
if (!completeResponse) {
|
|
334
|
+
return new Promise((resolve, reject) => {
|
|
335
|
+
reject(`Fail to complete multipart upload for file ${fileName}`);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
if (isGetInfo === false) {
|
|
339
|
+
return new Promise((resolve, reject) => {
|
|
340
|
+
resolve(fileName);
|
|
341
|
+
});
|
|
342
|
+
} else {
|
|
343
|
+
const objectDetail: ObjectDetail | null = await this.getObjectDetail(
|
|
344
|
+
bucketName,
|
|
345
|
+
fileName,
|
|
346
|
+
accessToken
|
|
347
|
+
);
|
|
348
|
+
if (!objectDetail) {
|
|
349
|
+
return new Promise((resolve, reject) => {
|
|
350
|
+
reject(`Fail to get object detail for file ${fileName}`);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
const fileModel = {
|
|
354
|
+
bucketName: bucketName,
|
|
355
|
+
fileName: fileName,
|
|
356
|
+
contentLength: buffer.length,
|
|
357
|
+
versionFile: objectDetail.versionId,
|
|
358
|
+
dateVersionFile: objectDetail.lastModified,
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
return new Promise((resolve, reject) => {
|
|
362
|
+
resolve(fileModel);
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
public async validateFileSize(fileName: string, fileSize: number) {
|
|
368
|
+
var sizeAllowed = await this.isFileSizeAllowed(fileSize);
|
|
369
|
+
if (!sizeAllowed) {
|
|
370
|
+
console.error(
|
|
371
|
+
`File size of ${fileName} too big to upload. Currently max file size allowed is ${
|
|
372
|
+
Number(ConfigFileRules.MaxChunkCountAllowed) *
|
|
373
|
+
Number(ConfigFileRules.ChunkSize)
|
|
374
|
+
} bytes`
|
|
375
|
+
);
|
|
376
|
+
// throw new OssApiError(`${requestId} File size too big to upload. Currently max file size allowed is ${Number(ConfigFileRules.MaxChunkCountAllowed) * Number(Constants.ChunkSize)} bytes`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private async isFileSizeAllowed(fileSize: number): Promise<boolean> {
|
|
381
|
+
const numberOfChunks: number = this.calculateNumberOfChunks(fileSize);
|
|
382
|
+
if (numberOfChunks > ConfigFileRules.MaxChunkCountAllowed) {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
public calculateNumberOfChunks(fileSize: number): number {
|
|
389
|
+
if (fileSize == 0) {
|
|
390
|
+
return 1;
|
|
391
|
+
}
|
|
392
|
+
var numberOfChunks: number = Math.trunc(
|
|
393
|
+
fileSize / ConfigFileRules.ChunkSize
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
if (fileSize % ConfigFileRules.ChunkSize != 0) {
|
|
397
|
+
numberOfChunks++;
|
|
398
|
+
}
|
|
399
|
+
return numberOfChunks;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
public async getObjectDetail(
|
|
403
|
+
bucketName: string,
|
|
404
|
+
fileName: string,
|
|
405
|
+
accessToken?: string
|
|
406
|
+
) {
|
|
407
|
+
try {
|
|
408
|
+
const url = `${Path.BaseURL}${Path.GetObjectDetail}`;
|
|
409
|
+
const headers: any = {
|
|
410
|
+
Authorization: `Bearer ${accessToken}`,
|
|
411
|
+
"Content-Type": "application/json",
|
|
412
|
+
"ngrok-skip-browser-warning": true,
|
|
413
|
+
};
|
|
414
|
+
let queryParams: any;
|
|
415
|
+
if (validString(bucketName)) {
|
|
416
|
+
queryParams = { ...queryParams, bucketName: bucketName };
|
|
417
|
+
}
|
|
418
|
+
if (validString(fileName)) {
|
|
419
|
+
queryParams = { ...queryParams, objectName: fileName };
|
|
420
|
+
}
|
|
421
|
+
const response = await fetch(
|
|
422
|
+
`${url}?${new URLSearchParams(queryParams)}`,
|
|
423
|
+
{
|
|
424
|
+
method: "GET",
|
|
425
|
+
headers: headers,
|
|
426
|
+
}
|
|
427
|
+
);
|
|
428
|
+
if (response.ok) {
|
|
429
|
+
const json: BaseReponseModel<ObjectDetail> =
|
|
430
|
+
(await response.json()) as BaseReponseModel<ObjectDetail>;
|
|
431
|
+
if (json.statusCode === ApiStatus.Success) {
|
|
432
|
+
return json.data;
|
|
433
|
+
} else {
|
|
434
|
+
console.error(
|
|
435
|
+
`Fail to generate presigned urls with message error: ${json.message}`
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
console.error(
|
|
440
|
+
`Fail to generate presigned urls with status ${response.statusText} and error: ${response.statusText}`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.error(`Error when get file info: ${error}`);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
public async initMultiPartUpload(
|
|
451
|
+
bucketName: string,
|
|
452
|
+
fileName: string,
|
|
453
|
+
accessToken?: string,
|
|
454
|
+
autoCreateBucket?: boolean | false,
|
|
455
|
+
cancellationToken?: AbortController
|
|
456
|
+
) {
|
|
457
|
+
try {
|
|
458
|
+
const url = `${Path.BaseURL}${Path.InitiateMultipartUpload}`;
|
|
459
|
+
const headers: any = {
|
|
460
|
+
Authorization: `Bearer ${accessToken}`,
|
|
461
|
+
"Content-Type": "application/json",
|
|
462
|
+
"ngrok-skip-browser-warning": true,
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
let queryParams: any;
|
|
466
|
+
if (validString(bucketName)) {
|
|
467
|
+
queryParams = { ...queryParams, bucketName: bucketName };
|
|
468
|
+
}
|
|
469
|
+
if (validString(fileName)) {
|
|
470
|
+
queryParams = { ...queryParams, objectName: fileName };
|
|
471
|
+
}
|
|
472
|
+
if (autoCreateBucket === true) {
|
|
473
|
+
queryParams = { ...queryParams, autoCreateBucket: autoCreateBucket };
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
queryParams = { ...queryParams, setPermission: false };
|
|
477
|
+
|
|
478
|
+
const response = await fetch(
|
|
479
|
+
`${url}?${new URLSearchParams(queryParams)}`,
|
|
480
|
+
{
|
|
481
|
+
method: "GET",
|
|
482
|
+
headers: headers,
|
|
483
|
+
}
|
|
484
|
+
);
|
|
485
|
+
if (response.ok) {
|
|
486
|
+
const json: BaseReponseModel<InitUploadResponse> =
|
|
487
|
+
(await response.json()) as BaseReponseModel<InitUploadResponse>;
|
|
488
|
+
if (json.statusCode === ApiStatus.Success) {
|
|
489
|
+
return json.data;
|
|
490
|
+
} else {
|
|
491
|
+
console.error(
|
|
492
|
+
`Fail to initiate multipart upload with message error: ${json.message}`
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
console.error(
|
|
497
|
+
`Fail to initiate multipart upload with status ${response.statusText} and error: ${response.statusText}`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
} catch (error) {
|
|
501
|
+
console.error(`Error when initiate multipart upload: ${error}`);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return undefined;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
public async generatePresignedUrls(
|
|
508
|
+
bucketName: string,
|
|
509
|
+
fileName: string,
|
|
510
|
+
initUpload: InitUploadResponse,
|
|
511
|
+
totalPart: number,
|
|
512
|
+
accessToken: string,
|
|
513
|
+
cancellationToken?: AbortController
|
|
514
|
+
) {
|
|
515
|
+
try {
|
|
516
|
+
const url = `${Path.BaseURL}${Path.GeneratePresignedUrls}`;
|
|
517
|
+
const headers = initUpload.headers;
|
|
518
|
+
|
|
519
|
+
let queryParams: any;
|
|
520
|
+
if (validString(bucketName)) {
|
|
521
|
+
queryParams = { ...queryParams, bucketName: bucketName };
|
|
522
|
+
}
|
|
523
|
+
if (validString(fileName)) {
|
|
524
|
+
queryParams = { ...queryParams, objectName: fileName };
|
|
525
|
+
}
|
|
526
|
+
if (validString(initUpload.uploadId)) {
|
|
527
|
+
queryParams = { ...queryParams, uploadId: initUpload.uploadId };
|
|
528
|
+
}
|
|
529
|
+
if (totalPart > 0) {
|
|
530
|
+
queryParams = { ...queryParams, totalPart: totalPart };
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const newHeaders: any = {
|
|
534
|
+
Authorization: `Bearer ${accessToken}`,
|
|
535
|
+
"ngrok-skip-browser-warning": true,
|
|
536
|
+
};
|
|
537
|
+
const response = await fetch(
|
|
538
|
+
`${url}?${new URLSearchParams(queryParams)}`,
|
|
539
|
+
{
|
|
540
|
+
method: "GET",
|
|
541
|
+
headers: { ...headers, ...newHeaders },
|
|
542
|
+
signal: cancellationToken?.signal
|
|
543
|
+
}
|
|
544
|
+
);
|
|
545
|
+
if (response.ok) {
|
|
546
|
+
const json: BaseReponseModel<string[]> =
|
|
547
|
+
(await response.json()) as BaseReponseModel<string[]>;
|
|
548
|
+
if (json.statusCode === ApiStatus.Success) {
|
|
549
|
+
return json.data;
|
|
550
|
+
} else {
|
|
551
|
+
console.error(
|
|
552
|
+
`Fail to generate presigned urls with message error: ${json.message}`
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
} else {
|
|
556
|
+
console.error(
|
|
557
|
+
`Fail to generate presigned urls with status ${response.statusText} and error: ${response.statusText}`
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return null;
|
|
562
|
+
} catch (error) {
|
|
563
|
+
console.error(`Error when generate presigned urls: ${error}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
public throwIfCancellationRequested(
|
|
568
|
+
cancellationToken: AbortController,
|
|
569
|
+
fileName: string
|
|
570
|
+
) {
|
|
571
|
+
if (cancellationToken.signal.aborted) {
|
|
572
|
+
console.error(`${fileName} Cancellation requested.`);
|
|
573
|
+
cancellationToken.signal.throwIfAborted();
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// public readFileBytes(file: Buffer, start: number, end: number): Buffer {
|
|
578
|
+
// const fileReader = file.subarray(start, end);
|
|
579
|
+
// return fileReader;
|
|
580
|
+
// }
|
|
581
|
+
|
|
582
|
+
public readFileBytes(file: File, start: number, end: number): Promise<ArrayBuffer> {
|
|
583
|
+
return new Promise((resolve, reject) => {
|
|
584
|
+
const blob = file.slice(start, end); // tạo blob từ phần file cần đọc
|
|
585
|
+
const reader = new FileReader();
|
|
586
|
+
|
|
587
|
+
reader.onload = () => {
|
|
588
|
+
if (reader.result instanceof ArrayBuffer) {
|
|
589
|
+
resolve(reader.result);
|
|
590
|
+
} else {
|
|
591
|
+
reject(new Error("Failed to read as ArrayBuffer"));
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
reader.onerror = () => reject(reader.error);
|
|
596
|
+
reader.readAsArrayBuffer(blob); // đọc phần blob thành ArrayBuffer
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
public async completeMultipartUpload(
|
|
601
|
+
bucketName: string,
|
|
602
|
+
fileName: string,
|
|
603
|
+
uploadId: string,
|
|
604
|
+
eTags: object,
|
|
605
|
+
accessToken?: string,
|
|
606
|
+
cancellationToken?: AbortController
|
|
607
|
+
) {
|
|
608
|
+
try {
|
|
609
|
+
const url = `${Path.BaseURL}${Path.CompleteMultipartUpload}`;
|
|
610
|
+
const headers: any = {
|
|
611
|
+
Authorization: `Bearer ${accessToken}`,
|
|
612
|
+
"Content-Type": "application/json",
|
|
613
|
+
"ngrok-skip-browser-warning": true,
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
let body: any;
|
|
617
|
+
if (validString(bucketName)) {
|
|
618
|
+
body = { ...body, bucketName: bucketName };
|
|
619
|
+
}
|
|
620
|
+
if (validString(fileName)) {
|
|
621
|
+
body = { ...body, objectName: fileName };
|
|
622
|
+
}
|
|
623
|
+
if (validString(uploadId)) {
|
|
624
|
+
body = { ...body, uploadId: uploadId };
|
|
625
|
+
}
|
|
626
|
+
if (eTags) {
|
|
627
|
+
body = { ...body, eTags: eTags };
|
|
628
|
+
}
|
|
629
|
+
const bodyString = JSON.stringify(body);
|
|
630
|
+
const response = await fetch(url, {
|
|
631
|
+
method: "POST",
|
|
632
|
+
headers: headers,
|
|
633
|
+
body: bodyString,
|
|
634
|
+
signal: cancellationToken?.signal
|
|
635
|
+
});
|
|
636
|
+
if (response.ok) {
|
|
637
|
+
const json: BaseReponseModel<CompleteMultipartUploadResponse> =
|
|
638
|
+
(await response.json()) as BaseReponseModel<CompleteMultipartUploadResponse>;
|
|
639
|
+
if (json.statusCode === ApiStatus.Success) {
|
|
640
|
+
return json.data;
|
|
641
|
+
} else {
|
|
642
|
+
console.error(
|
|
643
|
+
`Fail to complete multiple part upload with message error: ${json.message}`
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
} else {
|
|
647
|
+
console.error(
|
|
648
|
+
`Fail to complete multiple part upload with status ${response.statusText} and error: ${response.statusText}`
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return false;
|
|
653
|
+
} catch (error) {
|
|
654
|
+
console.error(`Error when complete multiple part upload: ${error}`);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
public async generateMultiPresignedUrl(
|
|
659
|
+
bucketName: string,
|
|
660
|
+
filesName: string[],
|
|
661
|
+
accessToken?: string,
|
|
662
|
+
cancellationToken?: AbortController
|
|
663
|
+
) {
|
|
664
|
+
try {
|
|
665
|
+
const url = `${Path.BaseURL}${Path.GenerateMultiPresignedURL}`;
|
|
666
|
+
// const url = `https://localhost:7193/api/Object/GenerateMultiPresignedURL`;
|
|
667
|
+
const headers: any = {
|
|
668
|
+
Authorization: `Bearer ${accessToken}`,
|
|
669
|
+
"Content-Type": "application/json",
|
|
670
|
+
"ngrok-skip-browser-warning": true,
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
const body = {
|
|
674
|
+
bucketName: bucketName,
|
|
675
|
+
objectNames: filesName,
|
|
676
|
+
setPermission: false
|
|
677
|
+
};
|
|
678
|
+
const bodyString = JSON.stringify(body);
|
|
679
|
+
|
|
680
|
+
const response = await fetch(url,
|
|
681
|
+
{
|
|
682
|
+
method: "POST",
|
|
683
|
+
headers: headers,
|
|
684
|
+
body: bodyString,
|
|
685
|
+
signal: cancellationToken?.signal
|
|
686
|
+
}
|
|
687
|
+
);
|
|
688
|
+
if (response.ok) {
|
|
689
|
+
const json: BaseReponseModel<MultiPresignURLResponse> =
|
|
690
|
+
(await response.json()) as BaseReponseModel<MultiPresignURLResponse>;
|
|
691
|
+
if (json.statusCode === ApiStatus.Success) {
|
|
692
|
+
return json.data;
|
|
693
|
+
} else {
|
|
694
|
+
console.error(
|
|
695
|
+
`Fail to generate multiple presigned url with message error: ${json.message}`
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
} else {
|
|
699
|
+
console.error(
|
|
700
|
+
`Fail to generate multiple presigned url with status ${response.statusText} and error: ${response.statusText}`
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return null;
|
|
705
|
+
} catch (error) {
|
|
706
|
+
console.error(`Error when generate multiple presigned url: ${error}`);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
//#endregion
|
|
710
|
+
}
|