← Back to list

🚀 “Stop Crashing Your Server: How to Stream Large Files Without Memory Spikes (2025 Guide)”

If you’ve ever tried serving large files — videos, ZIPs, backups, or logs — you’ve probably noticed something scary:

CodeTalks in Towards Dev · 2025-08-17 14:16 · 1 claps · 3.0 min read paywalled
#crashing #servers #large-file #streaming #memories
Open on Medium ↗
Wiki topics: OPS · LLMOps & Inference 🎬 · Film & Television

🚀 “Stop Crashing Your Server: How to Stream Large Files Without Memory Spikes (2025 Guide)”

🚀 “Stop Crashing Your Server: How to Stream Large Files Without Memory Spikes (2025 Guide)”

🚀 “Stop Crashing Your Server: How to Stream Large Files Without Memory Spikes (2025 Guide)”

If you’ve ever tried serving large files — videos, ZIPs, backups, or logs — you’ve probably noticed something scary:

  • The server slows down…
  • RAM usage shoots up…
  • Sometimes the app even crashes.

Why? Because too many developers make the same mistake: loading the entire file into memory before sending it.

In this blog, we’ll explore:

  1. Why memory spikes happen.
  2. The right way to stream files in Node.js, Python (FastAPI), and Spring Boot.
  3. Real-world optimizations (range requests, chunk sizes, compression).
  4. Extra code samples for production-ready setups.

Let’s go 👇

❌ The Wrong Way: Loading Entire File

Many tutorials show this approach, but it’s a trap:

Node.js

// ❌ Bad: Loads entire file into memory
const fs = require("fs");
const express = require("express");
const app = express();

app.get("/download", (req, res) => {
  const file = fs.readFileSync("huge.zip"); // Blocks and loads whole file
  res.send(file);
});

app.listen(3000, () => console.log("Server running..."));

Python

# ❌ Bad: Loads full file before sending
from fastapi import FastAPI
from fastapi.responses import FileResponse

app = FastAPI()

@app.get("/download")
def download_file():
    return FileResponse("huge.zip")  # not memory-friendly for very large files

Spring Boot

// ❌ Bad: ByteArrayResource loads entire file into memory
@GetMapping("/bad-download")
public ResponseEntity<Resource> badDownload() throws IOException {
    Path path = Paths.get("huge.zip");
    byte[] data = Files.readAllBytes(path); // Loads entire file
    ByteArrayResource resource = new ByteArrayResource(data);

    return ResponseEntity.ok()
            .contentLength(data.length)
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=huge.zip")
            .body(resource);
}

👉 Looks fine for small files. But with multi-GB files, your server’s RAM explodes.

✅ The Right Way: Streaming in Chunks

Instead of loading everything, streaming reads small chunks and sends them piece by piece.

🔹 Node.js (Express.js) Streaming

const fs = require("fs");
const express = require("express");
const app = express();

app.get("/download", (req, res) => {
  const filePath = "huge.zip";

  // Create a readable stream
  const stream = fs.createReadStream(filePath);

  // Set headers so browser knows it’s a download
  res.setHeader("Content-Disposition", "attachment; filename=huge.zip");
  res.setHeader("Content-Type", "application/zip");

  // Pipe file stream directly to response
  stream.pipe(res);

  // Handle errors
  stream.on("error", (err) => {
    console.error("Stream error:", err);
    res.status(500).send("File not found or error reading file");
  });
});

app.listen(3000, () => console.log("Server is running..."));

👉 Memory stays stable because only small chunks (default 64KB) are in memory.

🔹 Python (FastAPI) Streaming

from fastapi import FastAPI, Response
from fastapi.responses import StreamingResponse

app = FastAPI()

def iterfile(file_path: str):
    with open(file_path, "rb") as f:
        while chunk := f.read(1024 * 1024):  # 1MB chunks
            yield chunk

@app.get("/download")
def download_file():
    return StreamingResponse(iterfile("huge.zip"),
                             media_type="application/zip",
                             headers={"Content-Disposition": "attachment; filename=huge.zip"})

👉 Here, the file is read in 1MB chunks, not fully loaded into RAM.

🔹 Spring Boot Streaming

@GetMapping("/download")
public ResponseEntity<Resource> downloadFile() throws IOException {
    File file = new File("huge.zip");
    InputStreamResource resource = new InputStreamResource(new FileInputStream(file));

    return ResponseEntity.ok()
            .contentLength(file.length())
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=huge.zip")
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(resource);
}

👉 This ensures only small portions are read into memory at a time.

🎬 Streaming Media with Range Requests

If you’re serving videos or audio, clients often request specific byte ranges (for seeking/fast-forward).

Node.js Example with Range

app.get("/video", (req, res) => {
  const path = "movie.mp4";
  const stat = fs.statSync(path);
  const fileSize = stat.size;
  const range = req.headers.range;

  if (range) {
    const parts = range.replace(/bytes=/, "").split("-");
    const start = parseInt(parts[0], 10);
    const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;

    const chunkSize = (end - start) + 1;
    const stream = fs.createReadStream(path, { start, end });

    res.writeHead(206, {
      "Content-Range": `bytes ${start}-${end}/${fileSize}`,
      "Accept-Ranges": "bytes",
      "Content-Length": chunkSize,
      "Content-Type": "video/mp4",
    });

    stream.pipe(res);
  } else {
    res.writeHead(200, {
      "Content-Length": fileSize,
      "Content-Type": "video/mp4",
    });
    fs.createReadStream(path).pipe(res);
  }
});

👉 This allows scrubbing through videos without loading everything.

⚡ Best Practices for Streaming Large Files

  1. Choose proper chunk size → 512KB — 1MB works best.
  2. Support range requests → Critical for media streaming.
  3. Enable compression → Gzip/Brotli for text files, skip for already-compressed media (ZIP, MP4).
  4. Close streams properly → Always handle error and close events.
  5. Monitor memory usage → Tools like pm2, New Relic, or Grafana help track RAM spikes.
  6. Use CDN where possible → Let CDNs offload file serving, and stream only when necessary.

🚀 Final Takeaway

Handling large files doesn’t have to be scary. Instead of crashing your server with memory-hungry code, you can:

  • Stream files in chunks,
  • Support range requests,
  • Optimize chunk sizes, and
  • Scale your app smoothly.

In 2025 and beyond, efficient streaming is the difference between apps that survive traffic spikes… and apps that collapse under load.

👉 Pro tip: Combine streaming with a CDN, and you’ve got performance + scalability covered.


메타데이터
post_id
8eedf0adfb64
slug
stop-crashing-your-server-how-to-stream-large-files-without-memory-spikes-2025-guide-8eedf0adfb64
url
https://towardsdev.com/stop-crashing-your-server-how-to-stream-large-files-without-memory-spikes-2025-guide-8eedf0adfb64
canonical_url
https://towardsdev.com/stop-crashing-your-server-how-to-stream-large-files-without-memory-spikes-2025-guide-8eedf0adfb64
author_url
https://medium.com/@tuteja_lovish
status
ok
fetched_at
2026-08-23 02:22:14