Detect when browser receives file download

前端 未结 22 1741
陌清茗
陌清茗 2020-11-21 04:55

I have a page that allows the user to download a dynamically-generated file. It takes a long time to generate, so I\'d like to show a \"waiting\" indicator. The problem is,

22条回答
  •  你的背包
    2020-11-21 05:39

    The core problem is that the web browser does not have an event that fires when page navigation is cancelled but does have an event that fires when a page completes loading. Anything outside of a direct browser event is going to be a hack with pros and cons.

    There are four known approaches to dealing with detecting when a browser download starts:

    1. Call fetch(), retrieve the entire response, attach an a tag with a download attribute, and trigger a click event. Modern web browsers will then offer the user the option to save the already retrieved file. There are several downsides with this approach:
    • The entire data blob is stored in RAM, so if the file is large, it will consume that much RAM. For small files, this probably isn't a deal breaker.
    • The user has to wait for the entire file to download before they can save it. They also can't leave the page until it completes.
    • The built-in web browser file downloader is not used.
    • A cross-domain fetch will probably fail unless CORS headers are set.
    1. Use an iframe + a server-side cookie. The iframe fires a load event if a page loads in the iframe instead of starting a download but it does not fire any events if the download starts. Setting a cookie with the web server can then be detected by Javascript in a loop. There are several downsides with this approach:
    • The server and client have to work in concert. The server has to set a cookie. The client has to detect the cookie.
    • Cross-domain requests won't be able to set the cookie.
    • There are limits to how many cookies can be set per domain.
    • Can't send custom HTTP headers.
    1. Use an iframe with URL redirection. The iframe starts a request and once the server has prepared the file, it dumps a HTML document that performs a meta refresh to a new URL, which triggers the download 1 second later. The load event on the iframe happens when the HTML document loads. There are several downsides with this approach:
    • The server has to maintain storage for the content being downloaded. Requires a cron job or similar to regularly clean up the directory.
    • The server has to dump out special HTML content when the file is ready.
    • The client has to guess as to when the iframe has actually made the second request to the server and when the download has actually started before removing the iframe from the DOM. This could be overcome by just leaving the iframe in the DOM.
    • Can't send custom HTTP headers.
    1. Use an iframe + XHR. The iframe triggers the download request. As soon as the request is made via the iframe, an identical request via XHR is made. If the load event on the iframe fires, an error has occurred, abort the XHR request, and remove the iframe. If a XHR progress event fires, then downloading has probably started in the iframe, abort the XHR request, wait a few seconds, and then remove the iframe. This allows for larger files to be downloaded without relying on a server-side cookie. There are several downsides with this approach:
    • There are two separate requests made for the same information. The server can distinguish the XHR from the iframe by checking the incoming headers.
    • A cross-domain XHR request will probably fail unless CORS headers are set. However, the browser won't know if CORS is allowed or not until the server sends back the HTTP headers. If the server waits to send headers until the file data is ready, the XHR can roughly detect when the iframe has started to download even without CORS.
    • The client has to guess as to when the download has actually started to remove the iframe from the DOM. This could be overcome by just leaving the iframe in the DOM.
    • Can't send custom headers on the iframe.

    Without an appropriate built-in web browser event, there aren't any perfect solutions here. However, one of the four methods above will likely be a better fit than the others depending on your use-case.

    Whenever possible, stream responses to the client on the fly instead of generating everything first on the server and then sending the response. Various file formats can be streamed such as CSV, JSON, XML, ZIP, etc. It really depends on finding a library that supports streaming content. When streaming the response as soon as the request starts, detecting the start of the download won't matter as much because it will start almost right away.

    Another option is to just output the download headers up front instead of waiting for all of the content to be generated first. Then generate the content and finally start sending to the client. The user's built-in downloader will patiently wait until the data starts arriving. The downside is that the underlying network connection could timeout waiting for data to start flowing (either on the client or server side).

提交回复
热议问题