-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
98 lines (86 loc) · 3.04 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import axios from 'axios';
import { useUploader } from '@pantoninho/use-uploader';
export function useS3MultipartUploader({
chunkSize = 10 * 1024 * 1024,
initializer,
getPresignedUrls,
finalizer,
threads,
uploadFile,
} = {}) {
const { upload, uploads, isUploading } = useUploader({
threads,
uploadFile,
});
const multiPartUploads = mergeMultipartUploads(uploads);
return {
uploads: multiPartUploads,
isUploading,
uploadFile: uploadPart,
upload: async (file) => {
const numberOfChunks = Math.ceil(file.size / chunkSize);
const uploadRequest = await initializer(file);
const urls = await getPresignedUrls(uploadRequest, numberOfChunks);
const chunks = urls.map((url, i) => {
const blob = file.slice(i * chunkSize, (i + 1) * chunkSize);
return { file: new File([blob], file.name), to: url };
});
return upload(chunks, {
onComplete: (responses) => {
return finalizer(
uploadRequest,
responses.map((r, i) => ({
ETag: r.data.ETag,
PartNumber: i + 1,
})),
);
},
});
},
};
}
async function uploadPart(part, to, onUploadProgress) {
const { headers } = await axios.put(to, part, { onUploadProgress });
return {
ETag: headers.etag.replaceAll('"', ''),
};
}
function mergeMultipartUploads(uploads) {
uploads = Object.keys(uploads).reduce((multiPartUploads, uploadId) => {
const upload = uploads[uploadId];
if (!multiPartUploads[upload.file.name]) {
multiPartUploads[upload.file.name] = { parts: [] };
}
multiPartUploads[upload.file.name].parts.push(upload);
return multiPartUploads;
}, {});
return Object.keys(uploads).reduce((multiPartUploads, fileKey) => {
const fileUploads = uploads[fileKey];
return {
...multiPartUploads,
[fileKey]: {
parts: fileUploads.parts,
isUploading: fileUploads.parts.some((part) => part.isUploading),
progress:
fileUploads.parts.reduce(
(loaded, part) => loaded + part.loaded,
0,
) /
fileUploads.parts.reduce(
(total, part) => total + part.total,
0,
),
loaded: fileUploads.parts.reduce(
(loaded, part) => loaded + part.loaded,
0,
),
total: fileUploads.parts.reduce(
(total, part) => total + part.total,
0,
),
error: fileUploads.parts.some((part) => part.error),
},
};
}, {});
}
export class InvalidPartResponseError extends Error {}