How to Upload Large Videos in Chunks Using Axios
Uploading large video files over standard HTTP connections often leads to network timeouts, high memory consumption, and failed transfers due to unstable connections. The standard procedure for overcoming these limitations is chunked or multipart uploading, where a large video file is sliced into smaller binary segments on the client side and uploaded sequentially or concurrently using Axios. This article outlines the step-by-step process of preparing file slices, transmitting them with Axios, tracking upload progress, and finalizing the reassembly on the server.
1. Slice the Video File into Chunks
The HTML5 File API allows you to treat a video file as a
Blob and slice it into byte ranges. Define a uniform chunk
size (typically between 1 MB and 10 MB depending on server
configurations) and calculate the total number of segments.
const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB per chunk
function createChunks(file) {
const chunks = [];
let currentByte = 0;
while (currentByte < file.size) {
const chunk = file.slice(currentByte, currentByte + CHUNK_SIZE);
chunks.push(chunk);
currentByte += CHUNK_SIZE;
}
return chunks;
}2. Initialize the Upload Session
Before sending raw binary segments, initiate an upload session with
the backend. This request notifies the server of the incoming file name,
total size, MIME type, and total chunk count. The server returns a
unique uploadId used to group the segments.
async function initUploadSession(file, totalChunks) {
const response = await axios.post('/api/upload/init', {
fileName: file.name,
fileSize: file.size,
totalChunks: totalChunks
});
return response.data.uploadId;
}3. Upload Segments Sequentially with Axios
Iterate through the array of chunks and send each segment as
multipart form data. Include metadata headers or form fields such as the
chunk index and the uploadId so the server can track and
store each piece accurately.
async function uploadChunks(chunks, uploadId, file) {
for (let index = 0; index < chunks.length; index++) {
const formData = new FormData();
formData.append('chunk', chunks[index]);
formData.append('chunkIndex', index);
formData.append('uploadId', uploadId);
await axios.post('/api/upload/chunk', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round(
((index * CHUNK_SIZE + progressEvent.loaded) / file.size) * 100
);
console.log(`Upload Progress: ${Math.min(percentCompleted, 100)}%`);
}
});
}
}4. Implement Retry Logic for Failed Chunks
To ensure resilience, wrap the chunk upload request in a retry mechanism. If a network blip occurs, only the affected chunk is retransmitted rather than restarting the entire video transfer.
async function uploadChunkWithRetry(formData, retries = 3) {
for (let attempt = 1; attempt <= retries; attempt++) {
try {
return await axios.post('/api/upload/chunk', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
} catch (error) {
if (attempt === retries) throw error;
await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
}
}
}5. Finalize and Merge Chunks
Once all segments have been successfully uploaded, send a completion
request containing the uploadId and file details. The
backend uses this signal to concatenate the temporary chunk files in
order and generate the final video file.
async function completeUpload(uploadId, fileName) {
const response = await axios.post('/api/upload/complete', {
uploadId,
fileName
});
return response.data;
}6. Executing the Complete Workflow
Combine all operations into a single orchestrator function:
async function handleVideoUpload(file) {
try {
const chunks = createChunks(file);
const uploadId = await initUploadSession(file, chunks.length);
await uploadChunks(chunks, uploadId, file);
const result = await completeUpload(uploadId, file.name);
console.log('Video upload and assembly complete:', result);
} catch (error) {
console.error('Upload failed:', error);
}
}