Uploading a large file in a single request is risky: if it drops, you start again from zero. Chunk upload splits the file into small pieces uploaded one at a time. If it fails, you pick up where you left off.
Resumable Chunk Upload is an implementation of that technique. Simple to configure, it exposes progress and time remaining, and handles resuming on its own.
Installation
Install with a package manager:
npm install resumable-chunk-uploadInclude it from a CDN:
<script src="https://cdn.jsdelivr.net/npm/resumable-chunk-upload/dist/uploader.min.js"></script>Simple usage
Let's build the interface:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Resumable chunk upload</title>
</head>
<body>
<input type="file" /> <br />
<button id="stop">Stop</button> <br />
Progress: <span id="progress">0</span> % <br />
Remaining: <span id="remaining">0</span> seconds <br />
feedback: <span id="feedback"></span>
<script src="https://cdn.jsdelivr.net/npm/resumable-chunk-upload/dist/uploader.min.js"></script>
<script src="app.js"></script>
</body>
</html>Say we start the upload once the file is added, in the app.js file
const inputNode = document.querySelector("input");
const progressNode = document.querySelector("#progress");
const remainingNode = document.querySelector("#remaining");
const feedbackNode = document.querySelector("#feedback");
const uploader = new Uploader()
.setUploadStatusUrl("http://localhost:9000/uploadStatus")
.setUploadUrl("http://localhost:9000/upload")
.setChunkSize(10 ** 3)
.onProgress((info) => {
progressNode.innerHTML = info.percent;
remainingNode.innerHTML = info.remaining;
}, 1000);
inputNode.addEventListener("change", (e) => {
uploader
.setFile(e.target.files[0])
.upload()
.then((xhr) => {
feedbackNode.insertAdjacentHTML(
"beforeend",
`success: ${JSON.stringify(xhr.response)}`
);
})
.catch((error) => {
if (error instanceof UploadError) {
// This is a custom error to make it easier to manage
}
feedbackNode.insertAdjacentHTML("beforeend", `failed: ${e}`);
});
});
document.querySelector("#stop").addEventListener("click", () => {
uploader.abort();
});Here are the steps required before starting the upload with the upload method:
- Create an uploader
- Add the file
- Add the URL for fetching the number of the last uploaded chunk
- Add the upload URL
Resumable Chunk Upload uses two APIs. When you start the upload, it calls the first API to get the number of the last uploaded chunk, then uploads the remaining chunks one by one through the second API until it's done. The system sends an ID with each request so the backend can identify the upload.
Backend
Resumable Chunk Upload focuses on JavaScript clients so that it can drop easily into different frontend frameworks. For the backend, this OpenApi documentation covers integrating the two APIs. You can also take inspiration from the examples that already exist.
Error handling
Every error is an instance of the UploadError class, which makes them easier to handle.
// ...
if (error instanceof UploadError) {
if (error.message === 'UPLOAD_ABORTED') {
// Do something when upload is aborted
}
}
// ...Note: see the documentation for the details.
