typescriptadvanced

Stream File Download

Express handler that streams a file to the client with proper headers, range support, and error handling.

typescript
import { Request, Response } from 'express';
import fs from 'fs';
import path from 'path';

export function streamDownload(uploadsDir: string) {
  return (req: Request, res: Response) => {
    const filename = path.basename(req.params.filename);
    const filePath = path.join(uploadsDir, filename);

    fs.stat(filePath, (err, stats) => {
      if (err || !stats.isFile()) {
        return res.status(404).json({ error: 'File not found' });
      }

      const size = stats.size;
      const range = req.headers.range;

      if (range) {
        const [startStr, endStr] = range.replace(/bytes=/, '').split('-');
        const start = parseInt(startStr, 10);
        const end = endStr ? parseInt(endStr, 10) : size - 1;

        res.writeHead(206, {
          'Content-Range': `bytes ${start}-${end}/${size}`,
          'Accept-Ranges': 'bytes',
          'Content-Length': end - start + 1,
          'Content-Type': 'application/octet-stream',
        });

        fs.createReadStream(filePath, { start, end }).pipe(res);
      } else {
        res.writeHead(200, {
          'Content-Length': size,
          'Content-Disposition': `attachment; filename=\"${filename}\"`,
          'Content-Type': 'application/octet-stream',
        });

        fs.createReadStream(filePath).pipe(res);
      }
    });
  };
}

// Usage:
// app.get('/download/:filename', streamDownload('./uploads'));

Use Cases

  • Large file downloads
  • Resumable downloads
  • Media streaming

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.