Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I am trying to get a download progress % for a huge file stored in my Google Drive unit when downloading from my Nodejs script.

So far I have written the code to download, which is working, however the on('data'....) part is never called.

const downloadFile = (file) => {
    const fileId = file.id;
    const fileName = path.join(basePathForStorage, file.name);
    const drive = google.drive({ version: 'v3', authorization });

    let progress = 0;

    return new Promise((resolve, reject) => { 
        drive.files.get(
            {
                auth: authorization,
                fileId: fileId,
                alt: 'media'
            },
            { responseType: "arraybuffer" },
            function (err, { data }) {
                
                fs.writeFile(fileName, Buffer.from(data), err => {

                    
                    // THIS PART DOES NOTHING
                    data.on('data',(d)=>{
                        progress += d.length;
                        console.log(`Progress: ${progress}`)
                    })
                    // --------------------

                    if (err) {
                        console.log(err);
                        return reject(err);
                    }
                    return resolve(fileName)
                });
            }
        );
    });
}

Looks like I can't find the way to show the progess of the download by calling on('data'....)...wondering now if this is the correct way to do this, or if this is even possible.

I tried putting the on('data'....) code as it is now inside the writeFile function but also inside the callback from drive.files.get and nothing works.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
400 views
Welcome To Ask or Share your Answers For Others

1 Answer

Here it comes some code sample to do that,
this example has three parts that need to be mentioned:

  • Create a stream to track our download progress
  • Create a method to get the file size
  • Create an event emitter to send back our progress to our FE

So we will get the following:

const downloadFile = async(file) => {
  const fileId = file.id
  const fileName = path.join(basePathForStorage, file.name)
  
  let progress = 0
  /**
   * ATTENTION: here you shall specify where your file will be saved, usually a .temp folder
   * Here we create the stream to track our download progress
   */
  const fileStream = fs.createWriteStream(path.join(__dirname, './temp/', filename))

  const fileSize = await getFileSize(file)
  // In here we listen to the stream writing progress 
  fileStream.on('data', (chunk) => {
    progress += chunk.length / fileSize
    console.log('progress', progress)
  })

  const drive = google.drive({
    version: 'v3',
    authorization
  })

  drive.files.get({
    auth: authorization,
    fileId: fileId,
    alt: 'media'
  }, {
    responseType: "stream"
  },
  (err, { data }) => 
    data
      .on('end', () => console.log('onCompleted'))
      .on('error', (err) => console.log('onError', err))
      .pipe(fileStream)
  )
}

The method to retrieve the file size:

const getFileSize = ({ fileId: id }) => {
  const drive = google.drive({
    version: 'v3',
    authorization
  })

  return new Promise((resolve, reject) => 
    drive.files.get({ 
      auth: authorization,
      fileId
    }, (err, metadata) {
      if (err) return reject(err)
      else resolve(metadata.size)
    })
}

This code sample give you the ability to get partial updates from your file download as you're creating a write stream (nodejs#createWriteStream)
So you will be able to track your file downloading progress.

But, still you have to continuosly send these changes to your client ( FE ).
So, you could create your own EventEmitter to track that.

And now our sample will be enchanced with the following:

In our endpoint:

import { EventEmitter } from 'events'

router.post('/myEndpoint', (req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' })

  const progressEvent = new EventEmitter()
  progressEvent.on('progress', (progress) => {
    if (progress === 100)
      res.end()
    // So, your FE side will be receiving this message continuosly
    else res.write(`{ progress: ${ progress } }`)
  })

  const file = req.body // or where you're getting your file from
  downloadFile(file, progressEvent)
})

In our download method:

const downloadFile = async(file, progressEvent) => {
  .
  .
  .
  fileStream.on('data', (chunk) => {
    progress += chunk.length / fileSize
    progressEvent.emit('progress', progress)
  .
  .
  .

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...