Learning Node.js - File Upload Management and Media Streaming
Episode 17 of 23

Learning Node.js - File Upload Management and Media Streaming

This episode covers file uploads and media: processing multipart forms with multer, limiting file size and type, streaming files from disk with sendFile, and supporting range requests for video and audio.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Almost every modern application needs to accept files: profile photos, document attachments, or videos uploaded by users. Managing uploads correctly isn't just about storing bytes — you must validate content, limit size, and stream large files without weighing on memory.

Episode 17 covers file and media management in Node.js: processing multipart forms with multer, limiting file size and type, streaming files from disk to the response, and supporting range requests so videos can be played from the middle. These are the skills a media API needs.

Multipart Forms and Multer

Why Multipart Is Needed

Files can't be sent as plain JSON. Browsers send files in multipart/form-data, a format that separates regular fields and files in one request. Multer is an Express middleware that processes this format:

Install multer
npm install multer

npm install multer adds the upload middleware. Multer parses the multipart body and provides uploaded files through req.file or req.files.

Receiving Your First File

JSUpload with multer
import multer from "multer";
 
const upload = multer({ dest: "uploads/" });
 
app.post("/api/upload", upload.single("gambar"), (req, res) => {
  console.log(req.file.originalname);
  res.status(201).json({
    nama: req.file.originalname,
    ukuran: req.file.size,
    path: req.file.path,
  });
});

upload.single("gambar") captures a single file from the field named gambar. After the middleware runs, req.file holds the file metadata: originalname, size, and path, where the file is stored on disk. For multiple files, use upload.array("gambar", 5).

Limiting File Size and Type

Validating in the Middle of the Process

Files without limits are a security and resource-abuse risk. Multer allows validating names and types, along with a size limit at the Express level:

JSLimiting type and size
const upload = multer({
  dest: "uploads/",
  limits: { fileSize: 5 * 1024 * 1024 },
  fileFilter: (req, file, cb) => {
    if (file.mimetype === "image/png" || file.mimetype === "image/jpeg") {
      cb(null, true);
    } else {
      cb(new Error("Tipe file tidak diizinkan"));
    }
  },
});

limits: { fileSize: 5 * 1024 * 1024 } rejects files over 5 megabytes, and fileFilter only accepts PNG and JPEG. Remember: mimetype can be forged by the client — in production, additional validation by reading the file's magic bytes is a safer practice (we'll discuss that in episode 20).

Handling Upload Errors

When a limit is exceeded or a type is rejected, multer's error is sent to the error handler:

JSError handler for uploads
app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    return res.status(400).json({ error: "Upload gagal: " + err.message });
  }
  next(err);
});

err instanceof multer.MulterError detects multer-specific errors like LIMIT_FILE_SIZE. Turning them into a clear 400 response is much better than letting a raw error leak to the client.

Streaming Files from Disk

Sending Large Files Without Loading Them into Memory

When sending a file back to the client, don't read the whole thing into memory. Use a stream or res.sendFile, which flows the file directly from disk:

JSSend a file with sendFile
import { createReadStream } from "node:fs";
 
app.get("/api/download/:nama", (req, res) => {
  res.download("uploads/" + req.params.nama);
});

res.download(path) sends the file with a Content-Disposition header that triggers a download in the browser, while res.sendFile displays it inline. Both use streaming behind the scenes — large files are never fully loaded into memory. Sanitizing req.params.nama is important so users can't break through to other file paths, a topic we'll cover in episode 20.

Streaming for Media

JSVideo streaming
app.get("/api/video/:nama", (req, res) => {
  const stream = createReadStream("uploads/" + req.params.nama);
  stream.pipe(res);
});

createReadStream(...) reads the file in small chunks and stream.pipe(res) flows them to the response. This approach keeps server memory low even while serving large videos simultaneously.

Range Requests for Media

Playing Videos from the Middle

Browsers play videos by sending a range request — a request for only part of a file (for example, bytes 1000 to 5000). Without range support, videos can't be skipped. A correct server must understand the Range header and reply with status 206:

JSHandling Range requests
import { stat } from "node:fs/promises";
 
app.get("/api/video/:nama", async (req, res) => {
  const berkas = "uploads/" + req.params.nama;
  const info = await stat(berkas);
 
  res.status(200);
  res.setHeader("Content-Length", info.size);
  res.setHeader("Accept-Ranges", "bytes");
  res.setHeader("Content-Type", "video/mp4");
  createReadStream(berkas).pipe(res);
});

The Accept-Ranges: bytes header tells the browser that the server supports partial files. Full Range support — parsing the header, replying with 206 Partial Content, and mapping stream positions — can get complex; in practice, consider middleware like express-serve-static-core or a CDN that already handles this maturely.

Closing

Here's what to take away:

  • Multipart forms are processed with multer in Express middleware.
  • limits.fileSize and fileFilter limit file size and type.
  • Limit uploads so the server doesn't run out of memory.
  • res.download and res.sendFile stream files from disk.
  • Stream media with createReadStream and pipe.
  • The Accept-Ranges header and 206 support video playback.

In the next episode, episode 18, we'll discuss API testing and integration with modern tools — the built-in node:test runner, testing HTTP endpoints with supertest, mocking for isolation, and measuring coverage. You'll lock down your API so changes don't break features.

Learning Node.js - File Upload Management and Media Streaming | Learn Node.js