1

I have a function that forces a file download in the browser, and it works on small files but on large files it doesn't seem to work. When the download starts I get Failed - No File, and I have narrowed it down to the reason is that I am setting a 206 status code (Partial Content). This used to work just fine, but now I am getting the No File message.

When changing it to a status code of 200 the file gets downloaded. When keeping the status code to 206 and removing the Content-Disposition, the file plays in the browser.

Is 206 even needed for large files when downloading?

Failed - No File

export class Server {
  private static async send(client: Client, req: http.IncomingMessage, res: http.ServerResponse) {
    let fileSize = client.response.contentLength
    let start = 0, end = fileSize - 1 < start ? start : fileSize - 1
    if (fileSize > (this.app.chunkSize || 5e5)) {
      let range = (req.headers.range || '') as string
      let positions = range.replace(/bytes=/, '').trim().split('-')
      start = parseInt(positions[0] || '0', 10)
      end = parseInt(positions[1] || (fileSize - 1).toString(), 10)
      let chunkSize = (end - start) + 1

      // Setting the Code to 206 here breaks
      client.response.setCode(206)
        .setHeaders({
          'Content-Range': `bytes ${start}-${end}/${fileSize}`,
          'Accept-Ranges': 'bytes',
          'Connection': 'Keep-Alive',
          'Content-Length': chunkSize
        })
    }

    res.writeHead(client.response.code, <any>headers)

    let { store, file } = client.response.fileStore

    let stream: fs.ReadStream = store.readStream(file, { start, end })
      .on('open', () => stream.pipe(res))
      .on('close', () => res.end())
      .on('error', err => res.end(err))
  }
}

The read stream here is pretty simple:

export default class extends Storage {
  public readStream(filePath: string, options?: FileReadOptions) {
    return fs.createReadStream(this.toPath(filePath), options)
  }
}

Headers

Request

Get Off My Lawn
  • 34,175
  • 38
  • 176
  • 338
  • Do you have the same result on every browser? If no, which ones fail? A plus could be to get a full snapshot of the request and of the response to check everything is fine in the exchange! – sjahan Sep 12 '19 at 15:23
  • Chrome shows no file and doesn't save anything. Firefox downloads an empty file to my `Downloads` folder. – Get Off My Lawn Sep 12 '19 at 15:26
  • I'm not sure what all your code is doing, but streaming a file to the response [looks a fair bit easier judging from this answer](https://stackoverflow.com/a/17818492/215552)... – Heretic Monkey Sep 12 '19 at 15:49
  • My code is sending the file in chunks to the browser that is what the `if` is doing (getting start and end points in the file). If this isn't done, large files will not stream/send fully to the browser. – Get Off My Lawn Sep 12 '19 at 15:53

1 Answers1

4

Send 206 only when explicitly requested by the browser, else send 200

if (!!req.headers.range) {
  // if and only if client sends us range http reader in request,
  // perform your logic, send 206
} else {
  // forget about partial content here,
  // pipe the file to res directly with 200
}

simmer
  • 2,639
  • 1
  • 18
  • 22
Ash
  • 41
  • 2