1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.Uploader = void 0;
|
7 |
|
8 | function _bluebirdLst() {
|
9 | const data = _interopRequireWildcard(require("bluebird-lst"));
|
10 |
|
11 | _bluebirdLst = function () {
|
12 | return data;
|
13 | };
|
14 |
|
15 | return data;
|
16 | }
|
17 |
|
18 | function _awsSdk() {
|
19 | const data = require("aws-sdk");
|
20 |
|
21 | _awsSdk = function () {
|
22 | return data;
|
23 | };
|
24 |
|
25 | return data;
|
26 | }
|
27 |
|
28 | function _builderUtil() {
|
29 | const data = require("builder-util");
|
30 |
|
31 | _builderUtil = function () {
|
32 | return data;
|
33 | };
|
34 |
|
35 | return data;
|
36 | }
|
37 |
|
38 | function _events() {
|
39 | const data = require("events");
|
40 |
|
41 | _events = function () {
|
42 | return data;
|
43 | };
|
44 |
|
45 | return data;
|
46 | }
|
47 |
|
48 | function _fsExtraP() {
|
49 | const data = require("fs-extra-p");
|
50 |
|
51 | _fsExtraP = function () {
|
52 | return data;
|
53 | };
|
54 |
|
55 | return data;
|
56 | }
|
57 |
|
58 | function _os() {
|
59 | const data = require("os");
|
60 |
|
61 | _os = function () {
|
62 | return data;
|
63 | };
|
64 |
|
65 | return data;
|
66 | }
|
67 |
|
68 | function _crypto() {
|
69 | const data = require("crypto");
|
70 |
|
71 | _crypto = function () {
|
72 | return data;
|
73 | };
|
74 |
|
75 | return data;
|
76 | }
|
77 |
|
78 | function _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 |
|
80 | const MAX_PUT_OBJECT_SIZE = 5 * 1024 * 1024 * 1024;
|
81 | const MAX_MULTIPART_COUNT = 10000;
|
82 | const MIN_MULTIPART_SIZE = 5 * 1024 * 1024;
|
83 | const commonUploadSize = 15 * 1024 * 1024;
|
84 |
|
85 | _awsSdk().config.setPromisesDependency(require("bluebird-lst"));
|
86 |
|
87 | class 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 |
|
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 |
|
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 |
|
303 | exports.Uploader = Uploader;
|
304 |
|
305 | function smallestPartSizeFromFileSize(fileSize) {
|
306 | const partSize = Math.ceil(fileSize / MAX_MULTIPART_COUNT);
|
307 | return partSize < MIN_MULTIPART_SIZE ? MIN_MULTIPART_SIZE : partSize;
|
308 | }
|
309 |
|
\ | No newline at end of file |