UNPKG

@uppy/aws-s3

Version:

Upload to Amazon S3 with Uppy

261 lines (256 loc) 12.8 kB
function _classPrivateFieldLooseBase(e, t) { if (!{}.hasOwnProperty.call(e, t)) throw new TypeError("attempted to use private field on non-instance"); return e; } var id = 0; function _classPrivateFieldLooseKey(e) { return "__private_" + id++ + "_" + e; } import { AbortController } from '@uppy/utils/lib/AbortController'; const MB = 1024 * 1024; const defaultOptions = { getChunkSize(file) { return Math.ceil(file.size / 10000); }, onProgress() {}, onPartComplete() {}, onSuccess() {}, onError(err) { throw err; } }; function ensureInt(value) { if (typeof value === 'string') { // @ts-expect-error TS is not able to recognize it's fine. return parseInt(value, 10); } if (typeof value === 'number') { // @ts-expect-error TS is not able to recognize it's fine. return value; } throw new TypeError('Expected a number'); } export const pausingUploadReason = Symbol('pausing upload, not an actual error'); /** * A MultipartUploader instance is used per file upload to determine whether a * upload should be done as multipart or as a regular S3 upload * (based on the user-provided `shouldUseMultipart` option value) and to manage * the chunk splitting. */ var _abortController = /*#__PURE__*/_classPrivateFieldLooseKey("abortController"); var _chunks = /*#__PURE__*/_classPrivateFieldLooseKey("chunks"); var _chunkState = /*#__PURE__*/_classPrivateFieldLooseKey("chunkState"); var _data = /*#__PURE__*/_classPrivateFieldLooseKey("data"); var _file = /*#__PURE__*/_classPrivateFieldLooseKey("file"); var _uploadHasStarted = /*#__PURE__*/_classPrivateFieldLooseKey("uploadHasStarted"); var _onError = /*#__PURE__*/_classPrivateFieldLooseKey("onError"); var _onSuccess = /*#__PURE__*/_classPrivateFieldLooseKey("onSuccess"); var _shouldUseMultipart = /*#__PURE__*/_classPrivateFieldLooseKey("shouldUseMultipart"); var _isRestoring = /*#__PURE__*/_classPrivateFieldLooseKey("isRestoring"); var _onReject = /*#__PURE__*/_classPrivateFieldLooseKey("onReject"); var _maxMultipartParts = /*#__PURE__*/_classPrivateFieldLooseKey("maxMultipartParts"); var _minPartSize = /*#__PURE__*/_classPrivateFieldLooseKey("minPartSize"); var _initChunks = /*#__PURE__*/_classPrivateFieldLooseKey("initChunks"); var _createUpload = /*#__PURE__*/_classPrivateFieldLooseKey("createUpload"); var _resumeUpload = /*#__PURE__*/_classPrivateFieldLooseKey("resumeUpload"); var _onPartProgress = /*#__PURE__*/_classPrivateFieldLooseKey("onPartProgress"); var _onPartComplete = /*#__PURE__*/_classPrivateFieldLooseKey("onPartComplete"); var _abortUpload = /*#__PURE__*/_classPrivateFieldLooseKey("abortUpload"); class MultipartUploader { constructor(data, options) { var _this$options, _this$options$getChun; Object.defineProperty(this, _abortUpload, { value: _abortUpload2 }); Object.defineProperty(this, _resumeUpload, { value: _resumeUpload2 }); Object.defineProperty(this, _createUpload, { value: _createUpload2 }); // initChunks checks the user preference for using multipart uploads (opts.shouldUseMultipart) // and calculates the optimal part size. When using multipart part uploads every part except for the last has // to be at least 5 MB and there can be no more than 10K parts. // This means we sometimes need to change the preferred part size from the user in order to meet these requirements. Object.defineProperty(this, _initChunks, { value: _initChunks2 }); Object.defineProperty(this, _abortController, { writable: true, value: new AbortController() }); Object.defineProperty(this, _chunks, { writable: true, value: [] }); Object.defineProperty(this, _chunkState, { writable: true, value: [] }); /** * The (un-chunked) data to upload. */ Object.defineProperty(this, _data, { writable: true, value: void 0 }); Object.defineProperty(this, _file, { writable: true, value: void 0 }); Object.defineProperty(this, _uploadHasStarted, { writable: true, value: false }); Object.defineProperty(this, _onError, { writable: true, value: void 0 }); Object.defineProperty(this, _onSuccess, { writable: true, value: void 0 }); Object.defineProperty(this, _shouldUseMultipart, { writable: true, value: void 0 }); Object.defineProperty(this, _isRestoring, { writable: true, value: void 0 }); Object.defineProperty(this, _onReject, { writable: true, value: err => (err == null ? void 0 : err.cause) === pausingUploadReason ? null : _classPrivateFieldLooseBase(this, _onError)[_onError](err) }); Object.defineProperty(this, _maxMultipartParts, { writable: true, value: 10000 }); Object.defineProperty(this, _minPartSize, { writable: true, value: 5 * MB }); Object.defineProperty(this, _onPartProgress, { writable: true, value: index => ev => { if (!ev.lengthComputable) return; _classPrivateFieldLooseBase(this, _chunkState)[_chunkState][index].uploaded = ensureInt(ev.loaded); const totalUploaded = _classPrivateFieldLooseBase(this, _chunkState)[_chunkState].reduce((n, c) => n + c.uploaded, 0); this.options.onProgress(totalUploaded, _classPrivateFieldLooseBase(this, _data)[_data].size); } }); Object.defineProperty(this, _onPartComplete, { writable: true, value: index => etag => { // This avoids the net::ERR_OUT_OF_MEMORY in Chromium Browsers. _classPrivateFieldLooseBase(this, _chunks)[_chunks][index] = null; _classPrivateFieldLooseBase(this, _chunkState)[_chunkState][index].etag = etag; _classPrivateFieldLooseBase(this, _chunkState)[_chunkState][index].done = true; const part = { PartNumber: index + 1, ETag: etag }; this.options.onPartComplete(part); } }); this.options = { ...defaultOptions, ...options }; // Use default `getChunkSize` if it was null or something (_this$options$getChun = (_this$options = this.options).getChunkSize) != null ? _this$options$getChun : _this$options.getChunkSize = defaultOptions.getChunkSize; _classPrivateFieldLooseBase(this, _data)[_data] = data; _classPrivateFieldLooseBase(this, _file)[_file] = options.file; _classPrivateFieldLooseBase(this, _onSuccess)[_onSuccess] = this.options.onSuccess; _classPrivateFieldLooseBase(this, _onError)[_onError] = this.options.onError; _classPrivateFieldLooseBase(this, _shouldUseMultipart)[_shouldUseMultipart] = this.options.shouldUseMultipart; // When we are restoring an upload, we already have an UploadId and a Key. Otherwise // we need to call `createMultipartUpload` to get an `uploadId` and a `key`. // Non-multipart uploads are not restorable. _classPrivateFieldLooseBase(this, _isRestoring)[_isRestoring] = options.uploadId && options.key; _classPrivateFieldLooseBase(this, _initChunks)[_initChunks](); } start() { if (_classPrivateFieldLooseBase(this, _uploadHasStarted)[_uploadHasStarted]) { if (!_classPrivateFieldLooseBase(this, _abortController)[_abortController].signal.aborted) _classPrivateFieldLooseBase(this, _abortController)[_abortController].abort(pausingUploadReason); _classPrivateFieldLooseBase(this, _abortController)[_abortController] = new AbortController(); _classPrivateFieldLooseBase(this, _resumeUpload)[_resumeUpload](); } else if (_classPrivateFieldLooseBase(this, _isRestoring)[_isRestoring]) { this.options.companionComm.restoreUploadFile(_classPrivateFieldLooseBase(this, _file)[_file], { uploadId: this.options.uploadId, key: this.options.key }); _classPrivateFieldLooseBase(this, _resumeUpload)[_resumeUpload](); } else { _classPrivateFieldLooseBase(this, _createUpload)[_createUpload](); } } pause() { _classPrivateFieldLooseBase(this, _abortController)[_abortController].abort(pausingUploadReason); // Swap it out for a new controller, because this instance may be resumed later. _classPrivateFieldLooseBase(this, _abortController)[_abortController] = new AbortController(); } abort(opts) { if (opts != null && opts.really) _classPrivateFieldLooseBase(this, _abortUpload)[_abortUpload]();else this.pause(); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types [Symbol.for('uppy test: getChunkState')]() { return _classPrivateFieldLooseBase(this, _chunkState)[_chunkState]; } } function _initChunks2() { const fileSize = _classPrivateFieldLooseBase(this, _data)[_data].size; const shouldUseMultipart = typeof _classPrivateFieldLooseBase(this, _shouldUseMultipart)[_shouldUseMultipart] === 'function' ? _classPrivateFieldLooseBase(this, _shouldUseMultipart)[_shouldUseMultipart](_classPrivateFieldLooseBase(this, _file)[_file]) : Boolean(_classPrivateFieldLooseBase(this, _shouldUseMultipart)[_shouldUseMultipart]); if (shouldUseMultipart && fileSize > _classPrivateFieldLooseBase(this, _minPartSize)[_minPartSize]) { // At least 5MB per request: let chunkSize = Math.max(this.options.getChunkSize(_classPrivateFieldLooseBase(this, _data)[_data]), // Math.max can take undefined but TS does not think so _classPrivateFieldLooseBase(this, _minPartSize)[_minPartSize]); let arraySize = Math.floor(fileSize / chunkSize); // At most 10k requests per file: if (arraySize > _classPrivateFieldLooseBase(this, _maxMultipartParts)[_maxMultipartParts]) { arraySize = _classPrivateFieldLooseBase(this, _maxMultipartParts)[_maxMultipartParts]; chunkSize = fileSize / _classPrivateFieldLooseBase(this, _maxMultipartParts)[_maxMultipartParts]; } _classPrivateFieldLooseBase(this, _chunks)[_chunks] = Array(arraySize); for (let offset = 0, j = 0; offset < fileSize; offset += chunkSize, j++) { const end = Math.min(fileSize, offset + chunkSize); // Defer data fetching/slicing until we actually need the data, because it's slow if we have a lot of files const getData = () => { const i2 = offset; return _classPrivateFieldLooseBase(this, _data)[_data].slice(i2, end); }; _classPrivateFieldLooseBase(this, _chunks)[_chunks][j] = { getData, onProgress: _classPrivateFieldLooseBase(this, _onPartProgress)[_onPartProgress](j), onComplete: _classPrivateFieldLooseBase(this, _onPartComplete)[_onPartComplete](j), shouldUseMultipart }; if (_classPrivateFieldLooseBase(this, _isRestoring)[_isRestoring]) { const size = offset + chunkSize > fileSize ? fileSize - offset : chunkSize; // setAsUploaded is called by listPart, to keep up-to-date the // quantity of data that is left to actually upload. _classPrivateFieldLooseBase(this, _chunks)[_chunks][j].setAsUploaded = () => { _classPrivateFieldLooseBase(this, _chunks)[_chunks][j] = null; _classPrivateFieldLooseBase(this, _chunkState)[_chunkState][j].uploaded = size; }; } } } else { _classPrivateFieldLooseBase(this, _chunks)[_chunks] = [{ getData: () => _classPrivateFieldLooseBase(this, _data)[_data], onProgress: _classPrivateFieldLooseBase(this, _onPartProgress)[_onPartProgress](0), onComplete: _classPrivateFieldLooseBase(this, _onPartComplete)[_onPartComplete](0), shouldUseMultipart }]; } _classPrivateFieldLooseBase(this, _chunkState)[_chunkState] = _classPrivateFieldLooseBase(this, _chunks)[_chunks].map(() => ({ uploaded: 0 })); } function _createUpload2() { this.options.companionComm.uploadFile(_classPrivateFieldLooseBase(this, _file)[_file], _classPrivateFieldLooseBase(this, _chunks)[_chunks], _classPrivateFieldLooseBase(this, _abortController)[_abortController].signal).then(_classPrivateFieldLooseBase(this, _onSuccess)[_onSuccess], _classPrivateFieldLooseBase(this, _onReject)[_onReject]); _classPrivateFieldLooseBase(this, _uploadHasStarted)[_uploadHasStarted] = true; } function _resumeUpload2() { this.options.companionComm.resumeUploadFile(_classPrivateFieldLooseBase(this, _file)[_file], _classPrivateFieldLooseBase(this, _chunks)[_chunks], _classPrivateFieldLooseBase(this, _abortController)[_abortController].signal).then(_classPrivateFieldLooseBase(this, _onSuccess)[_onSuccess], _classPrivateFieldLooseBase(this, _onReject)[_onReject]); } function _abortUpload2() { _classPrivateFieldLooseBase(this, _abortController)[_abortController].abort(); this.options.companionComm.abortFileUpload(_classPrivateFieldLooseBase(this, _file)[_file]).catch(err => this.options.log(err)); } export default MultipartUploader;