diff --git a/packages/@uppy/aws-s3-multipart/src/MultipartUploader.js b/packages/@uppy/aws-s3-multipart/src/MultipartUploader.js index 68ca7fc714..b22728d1f2 100644 --- a/packages/@uppy/aws-s3-multipart/src/MultipartUploader.js +++ b/packages/@uppy/aws-s3-multipart/src/MultipartUploader.js @@ -26,23 +26,41 @@ function ensureInt (value) { 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. + */ class MultipartUploader { #abortController = new AbortController() + /** @type {import("../types/chunk").Chunk[]} */ #chunks + /** @type {{ uploaded: number, etag?: string, done?: boolean }[]} */ #chunkState + /** + * The (un-chunked) data to upload. + * + * @type {Blob} + */ #data + /** @type {import("@uppy/core").UppyFile} */ #file - #uploadPromise + /** @type {boolean} */ + #uploadHasStarted = false + /** @type {(err?: Error | any) => void} */ #onError + /** @type {() => void} */ #onSuccess + /** @type {typeof import('../types/index').AwsS3MultipartOptions["shouldUseMultipart"]} */ #shouldUseMultipart #onReject = (err) => (err?.cause === pausingUploadReason ? null : this.#onError(err)) @@ -119,13 +137,14 @@ class MultipartUploader { } #createUpload () { - this.#uploadPromise = this + this .options.companionComm.uploadFile(this.#file, this.#chunks, this.#abortController.signal) .then(this.#onSuccess, this.#onReject) + this.#uploadHasStarted = true } #resumeUpload () { - this.#uploadPromise = this + this .options.companionComm.resumeUploadFile(this.#file, this.#chunks, this.#abortController.signal) .then(this.#onSuccess, this.#onReject) } @@ -158,7 +177,7 @@ class MultipartUploader { } start () { - if (this.#uploadPromise) { + if (this.#uploadHasStarted) { if (!this.#abortController.signal.aborted) this.#abortController.abort(pausingUploadReason) this.#abortController = new AbortController() this.#resumeUpload() diff --git a/packages/@uppy/aws-s3-multipart/src/index.js b/packages/@uppy/aws-s3-multipart/src/index.js index 44d762f37e..6dc5719df1 100644 --- a/packages/@uppy/aws-s3-multipart/src/index.js +++ b/packages/@uppy/aws-s3-multipart/src/index.js @@ -239,6 +239,12 @@ class HTTPCommunicationQueue { }).abortOn(signal) } + /** + * @param {import("@uppy/core").UppyFile} file + * @param {import("../types/chunk").Chunk[]} chunks + * @param {AbortSignal} signal + * @returns {Promise} + */ async uploadFile (file, chunks, signal) { throwIfAborted(signal) if (chunks.length === 1 && !chunks[0].shouldUseMultipart) { @@ -281,6 +287,14 @@ class HTTPCommunicationQueue { return this.#sendCompletionRequest(file, { key, uploadId, parts, signal }).abortOn(signal) } + /** + * + * @param {import("@uppy/core").UppyFile} file + * @param {number} partNumber + * @param {import("../types/chunk").Chunk} chunk + * @param {AbortSignal} signal + * @returns {Promise} + */ async uploadChunk (file, partNumber, chunk, signal) { throwIfAborted(signal) const { uploadId, key } = await this.getUploadId(file, signal) diff --git a/packages/@uppy/aws-s3-multipart/types/chunk.d.ts b/packages/@uppy/aws-s3-multipart/types/chunk.d.ts new file mode 100644 index 0000000000..cde2ffc351 --- /dev/null +++ b/packages/@uppy/aws-s3-multipart/types/chunk.d.ts @@ -0,0 +1,6 @@ +export interface Chunk { + getData: () => Blob + onProgress: (ev: ProgressEvent) => void + onComplete: (etag: string) => void + shouldUseMultipart: boolean +}