Handle MultipleUpload file uploads with Express and the
multer middleware for multipart parsing.
This guide covers both standard uploads and chunked uploads with resume support.
Use multer to handle multipart/form-data. Files are saved to disk with a unique GUID filename.
const express = require("express");
const multer = require("multer");
const path = require("path");
const crypto = require("crypto");
const fs = require("fs");
const cors = require("cors");
const app = express();
const UPLOAD_DIR = path.join(__dirname, "uploads");
if (!fs.existsSync(UPLOAD_DIR)) fs.mkdirSync(UPLOAD_DIR, { recursive: true });
app.use(cors({
allowedHeaders: [
"Content-Type", "X-Upload-Id", "X-Chunk-Index",
"X-Chunk-Count", "X-File-Name", "X-File-Size"
]
}));
const storage = multer.diskStorage({
destination: UPLOAD_DIR,
filename: (req, file, cb) => {
const guid = crypto.randomUUID();
const ext = path.extname(file.originalname);
req.fileGuid = guid;
cb(null, guid + ext);
}
});
const upload = multer({
storage,
limits: { fileSize: 200 * 1024 * 1024 } // 200 MB
});
app.post("/upload", upload.single("file"), (req, res) => {
res.json({
success: true,
fileGuid: req.fileGuid,
fileName: req.file.originalname,
fileSize: req.file.size
});
});
For large files, MultipleUpload sends raw body chunks with custom headers. Store each chunk and assemble the final file once all chunks arrive.
const CHUNK_DIR = path.join(UPLOAD_DIR, "chunks");
app.post("/upload-chunk", express.raw({ type: "*/*", limit: "50mb" }), (req, res) => {
const uploadId = req.headers["x-upload-id"];
const chunkIndex = parseInt(req.headers["x-chunk-index"], 10);
const chunkCount = parseInt(req.headers["x-chunk-count"], 10);
const fileName = req.headers["x-file-name"];
const fileSize = parseInt(req.headers["x-file-size"], 10);
const sessionDir = path.join(CHUNK_DIR, uploadId);
if (!fs.existsSync(sessionDir)) fs.mkdirSync(sessionDir, { recursive: true });
// Save this chunk
fs.writeFileSync(path.join(sessionDir, `chunk_${chunkIndex}`), req.body);
const received = fs.readdirSync(sessionDir).length;
if (received === chunkCount) {
// Assemble final file
const fileGuid = crypto.randomUUID();
const ext = path.extname(fileName);
const finalPath = path.join(UPLOAD_DIR, fileGuid + ext);
const output = fs.createWriteStream(finalPath);
for (let i = 0; i < chunkCount; i++) {
const chunkData = fs.readFileSync(path.join(sessionDir, `chunk_${i}`));
output.write(chunkData);
}
output.end();
// Clean up chunks
fs.rmSync(sessionDir, { recursive: true });
return res.json({
success: true,
fileGuid: fileGuid,
fileName: fileName,
fileSize: fileSize
});
}
res.json({ success: true, chunksReceived: received });
});
app.listen(3000, () => console.log("Express upload server on http://localhost:3000"));
Both endpoints must return this JSON structure on successful upload completion.
{
"success": true,
"fileGuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"fileName": "photo.jpg",
"fileSize": 204800
}
Initialize MultipleUpload on the client, pointing to your Express endpoints.
<link rel="stylesheet" href="multipleupload.css" />
<div id="uploader"></div>
<script src="multipleupload.js"></script>
<script>
var uploader = new MultipleUpload("#uploader", {
uploadUrl: "http://localhost:3000/upload",
chunkUploadUrl: "http://localhost:3000/upload-chunk",
chunkSize: 2 * 1024 * 1024, // 2 MB chunks
onFileUploaded: function (file, response) {
console.log("Uploaded:", response.fileName, response.fileGuid);
}
});
</script>