Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/cancel-parallel-chunks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"dropzone": patch
---

Fix `cancelUpload` leaving parallel chunks uploading.

`file.xhr` only holds the request that started last, so cancelling a chunked upload running with `parallelChunkUploads` aborted that one request and left every other in-flight chunk streaming to the server — burning the user's bandwidth and writing orphaned chunks for a file the UI already showed as canceled.

Every chunk keeps its own request, so `cancelUpload` now aborts all of the ones still running. Uploads that are not chunked are unaffected.
13 changes: 12 additions & 1 deletion packages/dropzone/src/dropzone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1321,7 +1321,18 @@ export default class Dropzone extends Emitter {
for (let groupedFile of groupedFiles) {
groupedFile.status = Dropzone.CANCELED;
}
if (typeof file.xhr !== "undefined") {
if (file.upload.chunked && file.upload.chunks) {
// `file.xhr` only ever holds the request that started last, so with
// `parallelChunkUploads` aborting it leaves every other chunk
// streaming to the server for a file the user has already canceled.
// Each chunk keeps its own request, so abort them all. See #2366.
for (let chunk of file.upload.chunks) {
if (chunk && chunk.xhr && chunk.status === Dropzone.UPLOADING) {
chunk.status = Dropzone.CANCELED;
chunk.xhr.abort();
}
}
} else if (typeof file.xhr !== "undefined") {
file.xhr.abort();
}
for (let groupedFile of groupedFiles) {
Expand Down
30 changes: 30 additions & 0 deletions packages/dropzone/test/unit-tests/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -2162,6 +2162,36 @@ describe("Dropzone", function () {
}, 10);
}));

it("should abort every chunk still in flight when the upload is canceled", () =>
new Promise((done) => {
dropzone.options.chunking = true;
dropzone.options.chunkSize = 1;
dropzone.options.parallelChunkUploads = 3;

let file = getMockFile("text/html", "chunked-file", ["abcdef"]);
dropzone.addFile(file);

setTimeout(function () {
// Three of the six chunks are in flight, each with its own
// request.
expect(requests.length).toBe(3);
expect(requests.map((request) => request.aborted)).toEqual([false, false, false]);

dropzone.cancelUpload(file);

expect(file.status).toBe(Dropzone.CANCELED);
// `file.xhr` only holds the request that started last, so the
// other two used to keep streaming to the server. See #2366.
expect(requests.map((request) => request.aborted)).toEqual([true, true, true]);
expect(file.upload.chunks.map((chunk) => chunk.status)).toEqual([
Dropzone.CANCELED,
Dropzone.CANCELED,
Dropzone.CANCELED,
]);
done();
}, 10);
}));

it("should never start fewer than one chunk", () =>
new Promise((done) => {
startChunked({ parallelChunkUploads: 0 });
Expand Down
Loading