-1

In my webapp the user has the option to download a file containing some data, which they do by clicking on a button. For small amounts of data the file starts downloading pretty much immediately and that shows in the browser's download area. Which is good.

For large amounts of data it can take the server a substantial amount of time to calculate the data, even before we start downloading. This is not good. I want to indicate that the calculation is in progress. However I don't want to put a "busy" indicator on my UI, because the action does not block the UI - the user should be able to do other things while the file is being prepared.

A good solution from my point of view would be to start the download process before I have finished the calculation. We always know (or can quickly calculate) the first few hundred bytes of the file. Is there a mechanism where I can have the server respond to a download request with those few bytes, thus starting the download and making the file show up in the download area, and provide the rest of the file when I have finished calculating it? I'm aware that it will look like the download is stalled, and that's not a problem.

I can make a pretty good estimate of the file size very quickly. I would prefer not to have to use a third-party package to achieve this, unless it's a very simple one. We are using Angular but happy to code raw JS if needed.

DJClayworth
  • 26,349
  • 9
  • 53
  • 79
  • Please include more detailes about you'r server side code, java, tomcat? Also please include some details about how the download works for you... do you you have you'r file stored inside a DB? – luiscla27 Apr 17 '20 at 14:36
  • 1
    Our server is C#, but I'm looking for a general solution. The file is not stored in a DB. We need to create it based on API arguments. – DJClayworth Apr 17 '20 at 14:40

2 Answers2

1

To indicate that the link points to a download on the client, the easiest way is the download attribute on the link. The presence of the attribute tells the browser not to unload the current tab or create a new one; the value of the attribute is the suggested filename.

For the back-end part, after setting the correct response headers, just write the data to the output stream as it becomes available.

Touffy
  • 6,309
  • 22
  • 28
  • Actually, the best practice (if you are generating a large amount of data) would be to *pipe* the data to the output stream while propagating backpressure all the way to the process that generates that data — to avoid generating much faster than the client can download, which would force your server to keep the excess in memory until it can be received. – Touffy Apr 20 '20 at 21:50
0

You asked for a general solution

1) First, at your HTML/JS you can prevent the UI from being blocked by setting you download target to any other WebPage, the preferred way for doing this is to set the target to an IFRAME:

<!-- your link must target the iframe "downloader-iframe" -->
<a src="../your-file-generator-api/some-args?a=more-args" target="downloader-iframe">Download</a>
<!-- you don't need the file to be shown -->
<iframe id="downloader-iframe" style="display: none"></iframe>

2) Second, at your back-end you'll have to use both Content-Disposition and Content-Length(optional) headers, be careful using the "length" one, if you miss calculate the fileSize it will not be downloaded. If you don't use Content-Length you'll not see the "downloading progress".

3) Third, at you'r back-end you have to make sure that you are writing your bytes directly at your response! that way your Browser and your Web-Server will know that the download is "in progress",

HOW this 3 steps are built will be up to you, frameworks and libraries you are using, for example Dojo & JQuery have great IFRAME manipulation utilities, all thought you can do the coding by yourself, this is a JQuery sample:

Using jQuery and iFrame to Download a File

Also:

Adding a "busy" animation is ok! you just have to make sure that it's not blocking you'r UI, something like this: enter image description here

luiscla27
  • 4,956
  • 37
  • 49