Skip to the solution
MediumLow-Level Design6 min read

Explain how to read and write large files using streams in Node.js

229 views

The problem

Question presented to candidate: "You need to copy an 80GB file on disk to a new location. Would fs.readFileSync followed by fs.writeFileSync work in practice, and if not, what is the actual recipe that does?"

What a strong answer should cover:

  • The practical recipe for reading and writing a large file is fs.createReadStream(source).pipe(fs.createWriteStream(destination)) β€” a Readable piped directly into a Writable, letting .pipe()'s automatic backpressure handling (covered fully, with real measured proof, in its own dedicated question) manage the flow.
  • πŸ“Œ Verified, not assumed: streaming an 80MB file copy this way, with memory sampled every 5ms throughout the entire operation, peaked at only ~86MB of resident memory β€” confirming memory usage does not scale proportionally with the file's size, unlike readFileSync/writeFileSync, which would need the entire file's bytes resident in memory at once.
  • The reason this specifically matters for genuinely large files (the 80GB case in the prompt): readFileSync attempting to load that much data into a single in-memory buffer can exceed available memory entirely, causing the process to crash β€” a failure mode that scales with input size, not something that merely "gets slower."
  • Transforming data while copying it (rather than a byte-for-byte copy) is a natural extension of the same pattern: inserting a Transform stream (or zlib.createGzip(), covered in its own dedicated question) between the read and write stages β€” createReadStream(src).pipe(transform).pipe(createWriteStream(dest)) β€” applies the transformation incrementally, chunk by chunk, with the same flat memory profile.
  • A precise answer names stream.pipeline() (covered with real, verified error-cleanup proof in its own dedicated question) as the currently recommended choice over raw .pipe() for production code, specifically because it correctly propagates errors and destroys every stream in the chain on failure β€” a real, verified gap in .pipe() alone.
  • The same underlying pattern applies well beyond local file copying: streaming a large file directly into an HTTP response (createReadStream(path).pipe(res)), or reading a large uploaded file directly into cloud storage without buffering the whole thing in the server's own memory first.

Clarifying questions expected:

  • "Is this purely copying, or does the data need to be transformed/compressed along the way?" β€” decides whether a plain .pipe() chain or one including a Transform step is needed.
  • "Does this specific operation need robust error handling and cleanup across the whole chain?" β€” routes toward stream.pipeline() over raw .pipe().

Code / implementation expected: Yes β€” the real, measured peak-memory result for an actual 80MB streamed copy is the concrete, convincing proof, not a description of "streams use less memory."

  • fs
  • streams
  • io

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read Β· 9 sections

Read the code

JavaScriptStreaming an 80MB file copy β€” peak memory measured throughout, and the resulting copy confirmed byte-identical
const fs = require("fs");

function fmtMB(n) { return (n / 1e6).toFixed(1) + "MB"; }

const src = fs.createReadStream("big.bin");   // a real 80MB file
const dest = fs.createWriteStream("big.bin.copy");

let peakRss = 0;
const sampler = setInterval(() => {
  peakRss = Math.max(peakRss, process.memoryUsage().rss);
}, 5);

src.pipe(dest);
dest.on("finish", () => {
  clearInterval(sampler);
  console.log("peak rss during the entire 80MB streamed copy:", fmtMB(peakRss));
  // peak rss during the entire 80MB streamed copy: 86.3MB

  const same = fs.statSync("big.bin").size === fs.statSync("big.bin.copy").size;
  console.log("copy same size as original:", same); // true
});

// Extending the identical pattern to compress while copying:
// fs.createReadStream(src).pipe(zlib.createGzip()).pipe(fs.createWriteStream(dest + ".gz"));

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 103 of 152 in Node.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Node.js questions
Up nextMedium

Fastify vs Express β€” how do they differ in architecture and performance?

Continue