Works with any backend npm install Zero dependencies
← All Backend Guides

Express.js

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.

Multipart Form Upload

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
  });
});

Chunked Upload

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"));

Expected JSON Response

Both endpoints must return this JSON structure on successful upload completion.

{
  "success": true,
  "fileGuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "fileName": "photo.jpg",
  "fileSize": 204800
}

Client-Side JavaScript

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>