Handling Large File Upload and Streaming Data with Express.js

In modern web applications, handling large files efficiently is crucial. Whether you’re building an image upload feature, processing large documents, or managing media streaming, you need to ensure that your server can manage large file uploads and streaming data without crashing or running into performance bottlenecks. In this chapter, we will dive deep into handling large file uploads and streaming data with Express.js. We'll start from the basics and cover advanced techniques, including the most efficient practices for large file handling.

By the end of this chapter, you will have all the knowledge you need to handle file uploads and streaming data in your Express.js applications, without needing to look up additional resources.​

Introduction to File Uploads and Streaming

File Upload Basics

Uploading files is a common task for modern web applications. However, uploading large files can cause performance issues such as timeouts, memory overflow, and high server load. Understanding how to manage file uploads and data streams efficiently is essential for building robust applications.

Streaming Data

Streaming refers to sending data in small chunks, rather than all at once. This is useful when dealing with large datasets or files. Unlike file uploads, streaming allows data to be processed as it arrives, which helps avoid issues like running out of memory or crashing due to large file sizes.

Setting Up Express.js for File Uploads

Before we get into handling large file uploads, let’s first set up a simple Express.js server for basic file uploads using Multer, a middleware for handling file uploads.

Installing Required Packages

				
					npm install express multer

				
			
  • express: Framework for building web applications.
  • multer: Middleware for handling file uploads.

Basic File Upload Setup

				
					const express = require('express');
const multer = require('multer');
const path = require('path');

const app = express();

// Setup multer storage configuration
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, './uploads');
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + path.extname(file.originalname)); // Ensure unique file names
  }
});

const upload = multer({ storage: storage });

// Basic file upload route
app.post('/upload', upload.single('file'), (req, res) => {
  res.send('File uploaded successfully');
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

				
			

Explanation of Code

  • multer.diskStorage: Configures where to save uploaded files and how to name them.
  • upload.single('file'): The single method is used when expecting a single file upload with the field name file.
  • res.send: Sends a success message after the file is uploaded.

With this setup, the server can accept file uploads, and files are saved in the ./uploads directory.

Handling Large File Uploads

Challenges with Large Files

Handling large files comes with its own set of challenges:

  1. Memory Consumption: Storing the entire file in memory can cause the server to crash or slow down.
  2. Timeouts: Large files may take longer to upload, leading to timeouts.
  3. Disk Space: Saving large files directly to disk without efficient management can fill up the server storage.

Strategies for Handling Large File Uploads

  • Streaming Uploads: Instead of loading the entire file into memory, we can stream the file to disk or process it in chunks.
  • Limiting File Size: You can set a maximum file size to prevent users from uploading extremely large files.
  • Handling Timeouts: Set appropriate timeout values to handle slow uploads.

Setting a File Size Limit in Multer

				
					const upload = multer({
  storage: storage,
  limits: { fileSize: 10 * 1024 * 1024 } // Limit file size to 10MB
});

				
			
  • limits: { fileSize: 10 * 1024 * 1024 }: This sets the file size limit to 10MB.

Example of Handling Large File Uploads with Streaming

When you need to process large files (like videos or images) without loading them fully into memory, streaming is an ideal solution.

Here’s how to handle file uploads using streams in Express.js:

				
					const fs = require('fs');
const multer = require('multer');
const express = require('express');

const app = express();
const upload = multer({ dest: 'uploads/' });

// Handling file stream directly to disk
app.post('/upload-stream', upload.single('file'), (req, res) => {
  const filePath = `uploads/${req.file.filename}`;
  const readStream = fs.createReadStream(filePath);
  const writeStream = fs.createWriteStream(`uploads/processed_${req.file.filename}`);

  // Pipe the readStream into a writeStream to process file data in chunks
  readStream.pipe(writeStream);

  writeStream.on('finish', () => {
    res.send('File uploaded and processed successfully');
  });

  writeStream.on('error', (err) => {
    res.status(500).send('File processing error');
  });
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

				
			

Explanation of Code

  • fs.createReadStream(filePath): This creates a readable stream to read the file data from disk.
  • fs.createWriteStream(writePath): This creates a writable stream to write processed data.
  • .pipe(): This method allows you to stream data from the read stream directly to the write stream.

In this example, we stream the uploaded file to disk and process it in chunks.

Advanced Streaming Techniques

Handling Multiple Large Files

When dealing with multiple large file uploads, you can modify the Express.js server to handle arrays of files in parallel using Multer’s array method

				
					app.post('/multi-upload', upload.array('files', 5), (req, res) => {
  // `req.files` contains an array of uploaded files
  console.log(req.files);
  res.send('Multiple files uploaded successfully');
});

				
			
  • upload.array('files', 5): This allows the upload of multiple files (up to 5 in this case).

Stream Data to Remote Storage

If your application needs to upload large files directly to a remote service (e.g., AWS S3, Google Cloud Storage), streaming the file to the remote storage can be more efficient than first saving the file locally.

Here’s an example of streaming a file to S3 using AWS SDK:

				
					const AWS = require('aws-sdk');
const multerS3 = require('multer-s3');
const multer = require('multer');

// Set up AWS S3 configuration
const s3 = new AWS.S3();
const upload = multer({
  storage: multerS3({
    s3: s3,
    bucket: 'my-bucket',
    key: function (req, file, cb) {
      cb(null, Date.now().toString()); // Use unique names for the files
    }
  })
});

app.post('/upload-s3', upload.single('file'), (req, res) => {
  res.send('File uploaded to S3 successfully');
});

				
			
  • multer-s3: This middleware allows streaming file uploads directly to an S3 bucket.

Handling Large Data Streams

Use Cases for Streaming

Streaming is used in scenarios such as:

  • Video/Audio Streaming: Stream media content to clients without fully loading the file into memory.
  • Real-Time Data Feeds: Stream data like logs or sensor readings to clients in real-time.

Setting Up Data Streams for Clients

For example, streaming large video files can be handled using Range Requests. This is a mechanism that allows clients to request specific parts (chunks) of a file.

Here’s how to implement range-based streaming for large video files in Express:

				
					const fs = require('fs');
const path = require('path');

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

  if (!range) {
    res.status(416).send('Range not specified');
    return;
  }

  const parts = range.replace(/bytes=/, "").split("-"); // Extract range info
  const start = parseInt(parts[0], 10);
  const end = parts[1] ? parseInt(parts[1], 10) : Math.min(start + 1000000, fileSize - 1);

  const stream = fs.createReadStream(videoPath, { start, end });
  res.writeHead(206, {
    "Content-Range": `bytes ${start}-${end}/${fileSize}`,
    "Accept-Ranges": "bytes",
    "Content-Length": end - start + 1,
    "Content-Type": "video/mp4",
  });
  stream.pipe(res);
});

				
			

Explanation of Code

  • Range Requests: This allows clients to request specific byte ranges, which helps with streaming large files without downloading the entire file.
  • createReadStream: Creates a readable stream to send the requested byte range.

Handling large file uploads and streaming data efficiently is crucial for modern web applications. With Express.js and tools like Multer and streaming techniques, you can ensure your applications perform well under heavy load without running into issues like memory overflow or slow performance. Happy coding !❤️

Table of Contents

Contact here

Copyright © 2025 Diginode

Made with ❤️ in India