Search code examples
javascriptnode.jsexpressnode-streams

Proper send file with NodeJS


Consider the following rather trivial middleware:

(req, res, next) => {
  const stream = fs.createReadStream(req.filePath)

  await new Promise((resolve, reject) => stream    
    .on('error', reject)
    .on('end', resolve)
    .pipe(res, { end: false })
    .on('error', reject)
    // 'close' is emitted if client prematurely disconnects.
    .on('close', resolve)
  res.end()
}

There are a few problems which I am unsure how to handle:

  • If res has already emitted 'close' before this middleware runs it will be stuck.
  • If res emits close or error how is the source stream cleaned up? autoClose will only run on end which will only run if all data has been read. However, if the target fails how is that propagated to the source? Should I always call destroy on the source stream?

Solution

  • This seems to do the trick:

    import onFinished from 'on-finished'
    
    (req, res, next) => {
      const stream = fs.createReadStream(req.filePath)
    
      onFinished(res, () => stream.destroy && stream.destroy())
    
      if (onFinished.isFinished(res)) {
        return
      }
    
      await new Promise((resolve, reject) => stream    
        .on('error', reject)
        .on('end', resolve)
        .pipe(res, { end: false })
        .on('error', reject)
        // 'close' is emitted if client prematurely disconnects.
        .on('close', resolve)
      res.end()
    }