eligrey / FileSaver.js

An HTML5 saveAs() FileSaver implementation
https://eligrey.com/blog/saving-generated-files-on-the-client-side/
Other
21.39k stars 4.38k forks source link

Is there a way to know when a download is started?? #796

Open jvalrog opened 1 year ago

jvalrog commented 1 year ago

My backend have to download data from several sources and build a zip file in realtime. It takes a few seconds to prepare the download, so when the user clicks the button to save it, it's very easy for users to spam the button and trigger several downloads.

I know I can just disable the button for a few seconds, but it's a guessing solution.

If an event triggered when the download actually starts I could keep the button disabled for the correct amount of time.

rgusseinov commented 1 year ago

Hi! You want to fetch download date and time when you clicked on save button ? Please clarify.

jvalrog commented 1 year ago

Hi, no, I mean an event. Because the download takes a few seconds to start, it would be great knowing when it's actually starting.

rgusseinov commented 1 year ago

What if we add performance.now() before and after script, like in below example.

document.querySelector('#download-file') .addEventListener('click', () => { var t0 = performance.now();

    var file = new File(["Hello, world!"], "hello world.txt", {type: "text/plain;charset=utf-8"});
    FileSaver.saveAs(file);

    var t1 = performance.now();
    console.log("Save method took " + (t1 - t0) + " milliseconds.")
});

Is this what you mean ?

jvalrog commented 1 year ago

mmm no, I mean this:

  1. The user clicks the download button.
  2. The server starts some processing that takes 5 seconds.
  3. During those 5 seconds there is no indication for the user that the download "is working".
  4. The user believes the action isn't working and clicks again, starting another download action.
  5. After 5 seconds, the browser starts the actual downloads (in this case, 2 downloads).

I wanted an event that happens only when the actual download starts. So I can disable the button and reenable when it's emitted.

I see your library uses xmlhttprequest, and there is an event for "progress" that could emit an event on the filesaver object for the first time or something like that.

image

jimmywarting commented 12 months ago

It shouldn't really be up to filesaver.js to download things. it should only save blobs

if you need more adv download compatibility. then use xhr and download things yourself.

const thingsToDownload = [
  'http://httpbin.org/image/png',
  'http://httpbin.org/image/jpeg',
  // ...
].entries()

const blobs = []

async function download (iterator, i) {
  for (let [index, item] of iterator) {
    await new Promise(rs => {
      const xhr = new XMLHttpRequest()
      xhr.open('GET', item)
      xhr.responseType = 'blob'
      xhr.onload = () => {
        blobs[index] = xhr.response
        rs()
      }
      xhr.onprogress = evt => {
        if (evt.lengthComputable) {
          console.log(`Worker#${i}: ${index},${item} ${evt.loaded}/${evt.total}`)
        } else {
          console.log(`Worker#${i}: ${index},${item} ${evt.loaded}`)
        }
      }
    })
  }
}

// download 2 files at a time concurrently
const workers = Array(2).fill(thingsToDownload).map(download)

disable_download_button()
await Promise.allSettled(workers)
const zipFile = await create_zip_file(blobs)
enable_download_button()
saveAs(zipFile, 'things.zip')
FaliKurve commented 8 months ago

Nice guide, thanks