UNPKG

8.25 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.Uploader = void 0;
7
8function _bluebirdLst() {
9 const data = _interopRequireWildcard(require("bluebird-lst"));
10
11 _bluebirdLst = function () {
12 return data;
13 };
14
15 return data;
16}
17
18function _awsSdk() {
19 const data = require("aws-sdk");
20
21 _awsSdk = function () {
22 return data;
23 };
24
25 return data;
26}
27
28function _builderUtil() {
29 const data = require("builder-util");
30
31 _builderUtil = function () {
32 return data;
33 };
34
35 return data;
36}
37
38function _events() {
39 const data = require("events");
40
41 _events = function () {
42 return data;
43 };
44
45 return data;
46}
47
48function _fsExtraP() {
49 const data = require("fs-extra-p");
50
51 _fsExtraP = function () {
52 return data;
53 };
54
55 return data;
56}
57
58function _os() {
59 const data = require("os");
60
61 _os = function () {
62 return data;
63 };
64
65 return data;
66}
67
68function _crypto() {
69 const data = require("crypto");
70
71 _crypto = function () {
72 return data;
73 };
74
75 return data;
76}
77
78function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
79
80const MAX_PUT_OBJECT_SIZE = 5 * 1024 * 1024 * 1024;
81const MAX_MULTIPART_COUNT = 10000;
82const MIN_MULTIPART_SIZE = 5 * 1024 * 1024;
83const commonUploadSize = 15 * 1024 * 1024;
84
85_awsSdk().config.setPromisesDependency(require("bluebird-lst"));
86
87class Uploader extends _events().EventEmitter {
88 constructor(s3, s3Options, localFile, contentLength, fileContent) {
89 super();
90 this.s3 = s3;
91 this.s3Options = s3Options;
92 this.localFile = localFile;
93 this.contentLength = contentLength;
94 this.fileContent = fileContent;
95 /** @readonly */
96
97 this.loaded = 0;
98 this.cancelled = false;
99 this.s3RetryCount = 3;
100 this.s3RetryDelay = 1000;
101 this.multipartUploadThreshold = 20 * 1024 * 1024;
102 this.multipartUploadSize = commonUploadSize;
103 this.multipartDownloadThreshold = 20 * 1024 * 1024;
104 this.multipartDownloadSize = commonUploadSize;
105
106 if (this.multipartUploadThreshold < MIN_MULTIPART_SIZE) {
107 throw new Error("Minimum multipartUploadThreshold is 5MB.");
108 }
109
110 if (this.multipartUploadThreshold > MAX_PUT_OBJECT_SIZE) {
111 throw new Error("Maximum multipartUploadThreshold is 5GB.");
112 }
113
114 if (this.multipartUploadSize < MIN_MULTIPART_SIZE) {
115 throw new Error("Minimum multipartUploadSize is 5MB.");
116 }
117
118 if (this.multipartUploadSize > MAX_PUT_OBJECT_SIZE) {
119 throw new Error("Maximum multipartUploadSize is 5GB.");
120 }
121 }
122
123 upload() {
124 var _this = this;
125
126 return (0, _bluebirdLst().coroutine)(function* () {
127 const fileContent = _this.fileContent;
128
129 if (fileContent != null) {
130 const hash = (0, _crypto().createHash)("md5");
131 hash.update(fileContent);
132 const md5 = hash.digest("base64");
133 yield _this.runOrRetry(() => _this.putObject(md5));
134 return;
135 }
136
137 if (_this.contentLength < _this.multipartUploadThreshold) {
138 const md5 = yield (0, _builderUtil().hashFile)(_this.localFile, "md5");
139 yield _this.runOrRetry(() => _this.putObject(md5));
140 return;
141 }
142
143 let multipartUploadSize = _this.multipartUploadSize;
144
145 if (Math.ceil(_this.contentLength / multipartUploadSize) > MAX_MULTIPART_COUNT) {
146 multipartUploadSize = smallestPartSizeFromFileSize(_this.contentLength);
147 }
148
149 if (multipartUploadSize > MAX_PUT_OBJECT_SIZE) {
150 throw new Error(`File size exceeds maximum object size: ${_this.localFile}`);
151 }
152
153 const data = yield _this.runOrRetry(() => _this.s3.createMultipartUpload(_this.s3Options).promise());
154 yield _this.multipartUpload(data.UploadId, multipartUploadSize);
155 })();
156 }
157
158 abort() {
159 this.cancelled = true;
160 }
161
162 putObject(md5) {
163 this.loaded = 0;
164 return new (_bluebirdLst().default)((resolve, reject) => {
165 this.s3.putObject(Object.assign({
166 Body: this.fileContent || (0, _fsExtraP().createReadStream)(this.localFile),
167 ContentMD5: md5
168 }, this.s3Options)).on("httpUploadProgress", progress => {
169 this.loaded = progress.loaded;
170 this.emit("progress");
171 }).send((error, data) => {
172 if (error == null) {
173 resolve(data);
174 } else {
175 reject(error);
176 }
177 });
178 });
179 }
180
181 multipartUpload(uploadId, multipartUploadSize) {
182 var _this2 = this;
183
184 return (0, _bluebirdLst().coroutine)(function* () {
185 let cursor = 0;
186 let nextPartNumber = 1;
187 const partsA = [];
188 const parts = [];
189
190 while (cursor < _this2.contentLength) {
191 const start = cursor;
192 let end = cursor + multipartUploadSize;
193
194 if (end > _this2.contentLength) {
195 end = _this2.contentLength;
196 }
197
198 cursor = end;
199 const part = {
200 PartNumber: nextPartNumber++
201 };
202 partsA.push(part);
203 parts.push({
204 start,
205 end,
206 part,
207 md5: ""
208 });
209 }
210
211 yield _bluebirdLst().default.map(parts, (() => {
212 var _ref = (0, _bluebirdLst().coroutine)(function* (it) {
213 // hashFile - both start and end are inclusive
214 it.md5 = yield (0, _builderUtil().hashFile)(_this2.localFile, "md5", "base64", {
215 start: it.start,
216 end: it.end - 1
217 });
218 });
219
220 return function (_x) {
221 return _ref.apply(this, arguments);
222 };
223 })(), {
224 concurrency: (0, _os().cpus)().length
225 });
226 yield _bluebirdLst().default.map(parts, it => _this2.makeUploadPart(it, uploadId), {
227 concurrency: 4
228 });
229 return yield _this2.runOrRetry(() => _this2.s3.completeMultipartUpload({
230 Bucket: _this2.s3Options.Bucket,
231 Key: _this2.s3Options.Key,
232 UploadId: uploadId,
233 MultipartUpload: {
234 Parts: partsA
235 }
236 }).promise());
237 })();
238 }
239
240 makeUploadPart(part, uploadId) {
241 const contentLength = part.end - part.start;
242 return this.runOrRetry(() => {
243 let partLoaded = 0;
244 return new (_bluebirdLst().default)((resolve, reject) => {
245 this.s3.uploadPart({
246 ContentLength: contentLength,
247 PartNumber: part.part.PartNumber,
248 UploadId: uploadId,
249 Body: (0, _fsExtraP().createReadStream)(this.localFile, {
250 start: part.start,
251 end: part.end - 1
252 }),
253 Bucket: this.s3Options.Bucket,
254 Key: this.s3Options.Key,
255 ContentMD5: part.md5
256 }).on("httpUploadProgress", progress => {
257 partLoaded = progress.loaded;
258 this.loaded += progress.loaded;
259 this.emit("progress");
260 }).send((error, data) => {
261 if (error == null) {
262 part.part.ETag = data.ETag;
263 resolve(data);
264 } else {
265 this.loaded -= partLoaded;
266 reject(error);
267 }
268 });
269 });
270 });
271 }
272
273 runOrRetry(task) {
274 var _this3 = this;
275
276 return (0, _bluebirdLst().coroutine)(function* () {
277 return new (_bluebirdLst().default)((resolve, reject) => {
278 let attemptNumber = 0;
279
280 const tryRun = () => {
281 if (_this3.cancelled) {
282 return;
283 }
284
285 task().then(resolve).catch(error => {
286 if (++attemptNumber >= _this3.s3RetryCount) {
287 reject(error);
288 } else if (_this3.cancelled) {
289 reject(new Error("cancelled"));
290 } else {
291 setTimeout(tryRun, _this3.s3RetryDelay);
292 }
293 });
294 };
295
296 tryRun();
297 });
298 })();
299 }
300
301}
302
303exports.Uploader = Uploader;
304
305function smallestPartSizeFromFileSize(fileSize) {
306 const partSize = Math.ceil(fileSize / MAX_MULTIPART_COUNT);
307 return partSize < MIN_MULTIPART_SIZE ? MIN_MULTIPART_SIZE : partSize;
308}
309//# sourceMappingURL=uploader.js.map
\No newline at end of file