Search code examples
reactjstypescriptelectronnode-fetch

Correct way to calculate download percentage with node-fetch


I have the following method installFile which downloads a file to disk from S3. The code works perfectly. I've removed a bunch of irrelevant code and only included the part that I'm struggling with.

What I'm trying to do is console.log the download percentage of this file as it is downloading.

Here is a selection of the code, in particular where I access the bytes recieves using res.body.read().

... other imports etc   
import fetch from 'node-fetch'


export const installFile = async (file: FileReference): Promise<FileInstalledEvent> => {
  const s3Link = await getDownloadLink(file.id as number)
  const res = await fetch(s3Link)

  const contentLengthHeader = res.headers.get('Content-Length')
  const resourceSize = parseInt(contentLengthHeader, 10)

  return new Promise((resolve, reject) => {

    /* Keep track of download progress */
    res.body.on('readable', () => {
      let chunk
      let recievedLength: number = 0
      let downloadProgressAsPercentage: number = 0
      
      while (null !== (chunk = res.body.read())) {
        console.log(`Received ${chunk.length} bytes of data.`)
        recievedLength += chunk.length
        console.log("recieved", recievedLength, "of", resourceSize, "bytes")

        downloadProgressAsPercentage = recievedLength / resourceSize * 100
        console.log("Download percentage:", downloadProgressAsPercentage, "%")

      }
    })
... after this I go on to handle the file etc etc (which works fine)

The file downloads fine, but this is how the console log looks. The numbers are really confusing to me :)

Received 16360 bytes of data.
recieved 16360 of 4874349 bytes
Download percentage: 0.3356345637130209 %
Received 16360 bytes of data.
recieved 16360 of 4874349 bytes
Download percentage: 0.3356345637130209 %
Received 9050 bytes of data.
recieved 9050 of 4874349 bytes
Download percentage: 0.18566581916887773 %
download finished

Why do I not hit 100% at the end?


Solution

  • You can use Math.floor() method.

    Math.floor((loaded / total) * 100)