Jesse Taber

if I call it a blog I'll feel bad when I don't update it every week
posts - 26, comments - 70, trackbacks - 0

My Links

News

Twitter












Tag Cloud

Archives

Image Galleries

Detecting the File Download Dialog In the Browser

Updated 2011-01-28 to reference the correct question stackoverflow.com

Updated 2011-10-02 With a link to a sample application on github.

In the web application I work on we sometimes need to create PDF documents based on some user input. In most cases, these documents are fairly simple and quick to create, allowing us to create the documents in memory and then send them back to the requesting user with a “Content Disposition: attachment” in the HTTP response. This causes the browser file download mechanism to kick in allowing the user to save or open the resulting PDF. This approach works great and I think has come to be the expected behavior for getting files in 3rd party formats (Word, Excel, Adobe PDF, etc.) from a web application.

Generating a file, however, can sometimes take a few seconds. This short duration is not necessarily long enough to justify offloading the file generation to a separate process (e.g. a service bus, a separate worker thread within your web server process, etc.) but is long enough that the user waiting for the download may think that nothing is happening and attempt to click the “create file” button again. We definitely do not want the user submitting a request to create the same file over and over again, so we might use something like the jQuery Block UI plug-in to display a “please wait” message and prevent further requests from being made from that browser. This works great, but you eventually need to un-block the UI so that your users can continue doing their work after downloading the file. Unfortunately, I don’t know of any way javascript tricks to detect when the file download dialog is displayed to the user. Without being able to hook in to that event, the UI will remain blocked forever, forcing the user to either close and re-open their browser or hit the back button to get away from the “Please Wait” message. You could use some kind of time out to automatically un-block the page, but in most cases you’d likely end up un-blocking too early or too late.

There are some ways to work around this by writing the file to disk or some caching mechanism and then providing a separate URL endpoint to download the finished file, but these approaches require what is, in my opinion, a non-trivial amount of server side code to accomplish. All I really want is a way to let the user know that we’re building their file and to keep them from hitting ‘submit’ multiple times. In seraching for this I discovered a question on Stack Overflow (originally found on another not-to-be-named site that scraped stackoverflow.com) where one of the answers presented an interesting idea for solving this problem. While we can’t use javascript to determine when the browser receives a response with the “content-disposition: attachment” header, we can use javascript to determine when the browser receives a cookie with a certain name and value. We can add a very minimal amount of code to our server side implementation to add a cookie to the file download response and then use javascript to equate the presence of that cookie with the fact that the file is available for download. Let’s take a look at some code:

The Client Side Setup

The code I’m showing here is adapted from an ASP .NET Web Forms application, but I’ll try to keep it as a generic looking as possible as this approach should work regardless of the platform you use. First, let’s see a simple HTML form that we might use to capture the needed input from the user to create our PDF file:

<form id="create_pdf_form"> <fieldset> <legend>Create Customer Info Sheet PDF</legend> <div> <label id="first_name_prompt_id" for="first_name_input_id">First Name</label> <input name="first_name_input" id="first_name_input_id"/> </div> <div> <label id="last_name_prompt_id" for="last_name_input_id">Last Name</label> <input name="last_name_input" id="last_name_input_id"/> </div> <input type="hidden" id="download_token_value_id"/> <input type="submit" value="Create File"/> </fieldset> </form>

Notice the ‘hidden’ input field I included in that form. We’ll use that field to provide a token value to be included in a cookie in the file download response. Now let’s look at the jQuery code that we’ll use when this form is initially submitted. Note that this requires jQuery 1.4.2, the jQuery Block UI plug-in, and the jQuery cookies plug-in.

$(document).ready(function () { $('#create_pdf_form').submit(function () { blockUIForDownload(); }); }); var fileDownloadCheckTimer; function blockUIForDownload() { var token = new Date().getTime(); //use the current timestamp as the token value $('#download_token_value_id').val(token); $.blockUI(); fileDownloadCheckTimer = window.setInterval(function () { var cookieValue = $.cookie('fileDownloadToken'); if (cookieValue == token) finishDownload(); }, 1000); }

First, we’re using jQuery to hook into the ‘submit’ event of the HTML form. This will get fired just prior to the form data being submitted to the server. When this happens we’re generating a token value using the current timestamp. This token can really be any arbitrary string value, but we do want to try and make it unique per request coming from the same browser, and using the timestamp is a pretty easy way to do this. We take that token and add it to the form within that hidden field that we created. This will ensure that the token value gets submitted up to the server (more on this later).

Next, we’re using the jQuery Block UI plug-in to block the user from submitting the form multiple times. The behavior of the Block UI plug-in is very configurable and I encourage you to go check out the documentation for details on all of the different ways you can customize what message is displayed to the user while the UI is blocked.

With the UI effectively blocked, we use the ‘window.setInterval’ function to create an interval timer. The first argument provided is the function that you want to have executed at each interval and the second argument is how long you want the interval to be in milliseconds. For our purposes we want some code to check for the presence of a cookie value every 1 second. We’re using the jQuery cookies plug-in to examine the value of a cookie named ‘fileDownloadToken’. When the value of that cookie matches the token value that we generated before (based on the current timestamp), we’re going to invoke a method called ‘finishDownload’. We’ll look at the finishDownload method in a bit. First, let’s see what we need to do on the server side.

The Server Side Setup

Again, I’ve adapted this example from an ASP .NET Web Forms application, so the code below is C#, but this approach should work for just about any web platform. I’ve taken out the particulars around how the file to be downloaded is actually created and written to the HTTP response, as that will vary from application to application.

var response = HttpContext.Current.Response; response.Clear(); response.AppendCookie(new HttpCookie("fileDownloadToken", downloadTokenValue); //downloadTokenValue will have been provided in the form submit via the hidden input field response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", desiredFileName)); //desiredFileName will be whatever the resutling file name should be when downloaded //Code to generate file and write file contents to response response.Flush();

What I like best about this approach is that you were going to have to write 99% of the above code one way or another. The only addition that had to be made to the server side functionality was the single line where we add the file download token cookie to the response. This is the cookie value that our javascript timer is polling for back on the browser while all of the server side code is building the file to be downloaded. Once the response is flushed back to the client, our timer will see that the cookie is present and invoke the ‘finishDownload’ method. Let’s take a look at that.

Finishing Up On The Client Side

Once the expected cookie value appears, all that’s left to do is a little bit of clean up in the ‘finishDownload’ function:

function finishDownload() { window.clearInterval(fileDownloadCheckTimer); $.cookie('fileDownloadToken', null); //clears this cookie value $.unblockUI(); }
All we’re doing here is clearing out the previously set interval timer, removing the cookie, and unblocking the UI so that the user can continue doing their work after they decide what to do with the downloaded file. All in all, we added a line or two to the server side code and a few lines of javascript to accomplish our goal of informing the user that we’re working on their file.

I’ve put together a full working sample of this approach in the form of an ASP .NET MVC 3 application that you check out on github: https://github.com/appakz/Detect-Browser-File-Download-Dialog-Sample

Print | posted on Thursday, October 28, 2010 4:38 PM |

Feedback

Gravatar

# re: Detecting the File Download Dialog In the Browser

This solution is perfect! Works great with Spring MVC and a JSP. Thank you!
11/3/2010 9:34 AM | Brian
Gravatar

# re: Detecting the File Download Dialog In the Browser

Nice, thank you very much, but doesn't work on ie6 for me.. IE8, FF, opera, chrome are good, but old ie6 not :-(
11/16/2010 9:00 AM | pietro
Gravatar

# re: Detecting the File Download Dialog In the Browser

@pietro: What is the issue you're seeing in IE 6? I've tested this out in IE 6 before and have had no problems with it. Drop me a line on the contact form of the blog and maybe I can help you.
http://geekswithblogs.net/GruffCode/contact.aspx
11/17/2010 10:58 AM | Jesse
Gravatar

# re: Detecting the File Download Dialog In the Browser

All is OK now.
I applied your demo program to my java program. So I set header, cookie...
You have response.Flush();
I was sending the file using OutputStream, so I didn't do this step. But it wasn't function in IE6.
So I tried response.flushBuffer(); in java and finally it works.
Thanks for great demo program Jesse ;-)
11/18/2010 7:21 AM | pietro
Gravatar

# re: Detecting the File Download Dialog In the Browser

Hi Pietro,

Glad that you are able to do it for java, can you pl share that code for java. Thanks

yuva
12/28/2010 12:07 AM | Yuva
Gravatar

# re: Detecting the File Download Dialog In the Browser

Can this be achieved without the use of JQuery?
12/28/2010 4:51 AM | Lakshmi Narayanan
Gravatar

# re: Detecting the File Download Dialog In the Browser

@Lakshmi: This same technique can be accomplished without jQuery, though you would end up having to write a lot more client-side code to make it work (unless you were using some other javascript library). I know and like jQuery, so that's what I used.
1/2/2011 8:44 PM | Jesse
Gravatar

# re: Detecting the File Download Dialog In the Browser

This was a huge help! Thank you so much!
1/26/2011 2:52 PM | DevFox
Gravatar

# re: Detecting the File Download Dialog In the Browser

Thank you for this very elegant solution!

However you reference a question from eFreedom which is wrong: this site only plagiarize stackoverflow.com and messes all search engines results. As indicated on eFreedom, this question is copied from
http://stackoverflow.com/questions/3055138/can-you-use-javascript-to-detect-a-file-download-window-created-server-side

p.s. : the comment form does not accept my email address since it has a “+” in it :(
1/28/2011 10:55 AM | Didier
Gravatar

# re: Detecting the File Download Dialog In the Browser

@Didier: Thanks for pointing me to the correct question on Stack Overflow. I've updated the post to reflect that. When I first published this I didn't know what eFreedom was, and didn't know any better. I'm glad to give proper credit to the site that actually contained the original question.
1/28/2011 11:03 AM | Jesse
Gravatar

# re: Detecting the File Download Dialog In the Browser

You rock! One other cool thing is that the cookie is available in ALL browser windows referencing the same domain/path, so you can monitor the presence of the cookie in the "main" window, but load the d/l into a separate window (which could be nice depending on your needs).
2/3/2011 10:11 AM | dmans
Gravatar

# re: Detecting the File Download Dialog In the Browser

This is a very nice solution. I was searching for this for some time until I found it. BTW, I am also using jQuery and blockUI on the client side, the server side is spring MVC. Thanks for sharing this with everybody.
2/21/2011 12:16 PM | Salman
Gravatar

# re: Detecting the File Download Dialog In the Browser

This is beautiful, thanks!
3/10/2011 1:12 PM | Gary
Gravatar

# re: Detecting the File Download Dialog In the Browser

You beauty! Simple and elegant solution. Thanks so much.
3/21/2011 12:16 AM | Mo
Gravatar

# re: Detecting the File Download Dialog In the Browser

very nice,hope this can help me.thanks for your great work,man.

can i add you on facebook:

my is : ZhongHui Duan
4/19/2011 5:11 AM | zhonghui
Gravatar

# re: Detecting the File Download Dialog In the Browser

This is great code - thank you!

For the php users out there, the php equivalent of

AppendCookie(new HttpCookie("fileDownloadToken", downloadTokenValue);

in php is:

setcookie ("fileDownloadToken", $downloadTokenValue);

CJA
4/27/2011 4:02 AM | cja
Gravatar

# re: Detecting the File Download Dialog In the Browser

What a great idea. This worked out perfectly for what I wanted to do. Thanks for sharing this technique.
4/27/2011 11:43 AM | TAmes
Gravatar

# re: Detecting the File Download Dialog In the Browser

Thanks... this was a great post... i have been struggling for this sort of solution. Thanks a lot for sharing.

for those who are facing issue in reading cookie...

Set cookie.HttpOnly = false

5/6/2011 9:42 AM | Adil
Gravatar

# re: Detecting the File Download Dialog In the Browser

Hi, I just come across your post, it's brilliant.
Until now I've been using the window.onblur event to detect when the download dialog appears and hide the "please wait" div. I works fine provided the user don't decide to read an email during the wait.
I will definitely try your solution, thanks!
6/4/2011 1:06 PM | paolosca
Gravatar

# re: Detecting the File Download Dialog In the Browser

Perfect solution. Adapted it to Java w/out any issues.
7/14/2011 4:37 PM | Yuriy Shikhanovich
Gravatar

# re: Detecting the File Download Dialog In the Browser

Works Perfect. Thanks a lot
7/27/2011 12:30 PM | Phani Kumar
Gravatar

# re: Detecting the File Download Dialog In the Browser

I have a ajaxForm , when the user submits the page it hits the sever url and necessary page gets generated. But with the example that you have shown here how do I open a pop up window for the incoming file to download.

Can you please provide an example how to achieve it.

My way of sending and opening popup is $('#reportForm').ajaxForm( {

dataType :'json',
type :'POST',
url : 'report/initialRequest.html',
beforeSubmit :validateSearchField,
ifModified : true,
success :function(data, textStatus, jqXHR){},
complete : function(jqXHR, textStatus) {
window.location.href = "report/initialRequest.html" + "?" + $('#reportForm').formSerialize();
$.unblockUI();
return false;
}
});

But in this case what happens its send in another request again and reprocess what it has done earlier.

Can you please help me on this?
7/30/2011 4:23 PM | srinivas
Gravatar

# re: Detecting the File Download Dialog In the Browser

What a brilliant hack. :) The only browser I was able to get working without this trick was IE9 using document.readyState. Doesn't work so much with Chrome/FireFox.
8/10/2011 11:53 PM | Blake
Gravatar

# re: Detecting the File Download Dialog In the Browser

Nice one. I was looking for exactly this. Thanks very much.
8/12/2011 7:03 AM | Pete
Gravatar

# re: Detecting the File Download Dialog In the Browser

That's awesome! Exactly what I was looking for.
Thank you very much for sharing this.

8/18/2011 10:09 AM | Mohammad
Gravatar

# re: Detecting the File Download Dialog In the Browser

Hi, I'd like to have the Java-JSP translation if possible. I tried to translate but it's not working for me. Thank you.
8/18/2011 12:32 PM | Marta Ramirez
Gravatar

# re: Detecting the File Download Dialog In the Browser

You are my hero. I have been struggling with this issue for months! Thanks!
9/1/2011 8:42 AM | BethW
Gravatar

# re: Detecting the File Download Dialog In the Browser

Brilliant! This is exactly what I needed. Thanks a bunch!
9/2/2011 6:13 PM | Vlad
Gravatar

# re: Detecting the File Download Dialog In the Browser

There seems to be a problem On safari for this solution. No cookie is written.
9/5/2011 6:22 AM | Wu Huajie
Gravatar

# re: Detecting the File Download Dialog In the Browser

I'm in total debt to you. I've been trying to get a simple, minimal method of doing this forever! thanks!
9/15/2011 2:12 PM | Paul J
Gravatar

# re: Detecting the File Download Dialog In the Browser

i have a similar problem, in my case , i have a small zip file say of 400-500 bytes generated on air and passed on to browser for download which opens up download popup, by this time backend already had updated with file consumption from client, now delimma is when users clicks cancel,i wish to let backend rollback my changes, how can this be achieved. i am sick of this issue kindly help.
9/17/2011 2:08 AM | strive
Gravatar

# re: Detecting the File Download Dialog In the Browser

You saved my life... :-)
9/24/2011 11:01 AM | Srinivas
Gravatar

# re: Detecting the File Download Dialog In the Browser

This solution works me fine in Chrome and FF, but I have a problem with IE9 (IE in general). When the response with the file comes it contains a set-cookie header with the proper value, but when I read the cookie with the jquery function it doesn't read the proper value (it reads the value of the cookie I set when I load the page to test that I'm readingg the cookie properly). It seems like the browser is not setting the cookie due to is a file download response, if i reload the page the cookie of the response is not set.

The server language is PHP, but the solution works fine in FF and Chrome.

Any idea?? Thanks in advance.
9/29/2011 9:07 AM | Enrique Andreu
Gravatar

# re: Detecting the File Download Dialog In the Browser

How to set cookie in asp application and how to read it in jquery?
I have set the cookie in asp like this Response.AddHeader "Set-Cookie", "fileDownloadToken=something;path = /;"

But Jquery is not reading this.

I am using IE 6.0.Please help me.
10/7/2011 3:31 AM | Ram
Gravatar

# re: Detecting the File Download Dialog In the Browser

It works perfectly! Thanks for sharing this great gem!

Cheers,
Lee
10/17/2011 3:09 PM | Shih-gian Lee
Gravatar

# re: Detecting the File Download Dialog In the Browser

How genius idea is!
Exactly what I was looking for!
Thank you so much. :D
10/19/2011 12:50 AM | herme
Gravatar

# re: Detecting the File Download Dialog In the Browser

the cookies was written to the client must be after file sending completely?
10/20/2011 12:24 PM | phil
Gravatar

# re: Detecting the File Download Dialog In the Browser

@phil - I don't think I understand your question. The cookies should be sent to the browser in the same HTTP Response that contains the "data" for the file to be downloaded. In this way the 'block UI' stuff should go away as soon as the file download dialog appears.
10/20/2011 4:46 PM | Jesse
Gravatar

# re: Detecting the File Download Dialog In the Browser

if the file not send completely(might be paused by the user).will the cookies be sent to the browser?
10/22/2011 12:50 AM | phil
Gravatar

# re: Detecting the File Download Dialog In the Browser

sorry I misunderstood this post, I thought this solution is used to detect whether file was already download.. but many thanks you share the very good idea.
10/22/2011 8:53 AM | Phil
Gravatar

# re: Detecting the File Download Dialog In the Browser

A great post. You said the sample application is adapted from web form version. Can you post or email me the web form version. I have a project still in ASP.NET 2.0. A lot of efforts is required to migrate to MVC. Thanks!
10/30/2011 5:15 AM | willie
Gravatar

# re: Detecting the File Download Dialog In the Browser

man, you saved my life! i spent two freakin' days on this, and your solution works perfectly. thank you!
11/17/2011 1:23 AM | fattynoparents
Gravatar

# re: Detecting the File Download Dialog In the Browser

Thanks a lot!
11/17/2011 8:22 PM | Yuri
Gravatar

# re: Detecting the File Download Dialog In the Browser

Works great except in IE for PDF files. I was able to open the PDF files before applying this solution, however, after changing the code to the above solution, I am getting 'There was an error opening this document. This file cannot be found.'

Error only occurs in IE for PDF files. Does anyone have a clue?
11/24/2011 12:52 AM | Steve
Gravatar

# re: Detecting the File Download Dialog In the Browser

You save my life! Thanks!!
11/24/2011 9:30 AM | Gabriele Gaggi
Gravatar

# re: Detecting the File Download Dialog In the Browser

One thing for sure..

YOU SAVED MY LIFE!!

a week of headache gone instantly once i found and adapt your concept to my code..

THANK YOU..

TERIMA KASIH
11/30/2011 12:47 AM | Denny Saviant
Gravatar

# re: Detecting the File Download Dialog In the Browser

Wow!! good solution ...
But..... this will work only if cookie is enabled. Right?
what if it is an environment where cookies are not enabled?
will the data be passed as url encoded?
12/5/2011 1:06 AM | stv
Gravatar

# re: Detecting the File Download Dialog In the Browser

@stv

This solution will only work for browsers with cookies enabled.
12/5/2011 8:58 AM | Jesse
Gravatar

# re: Detecting the File Download Dialog In the Browser

You save my life hero
Thank you a lot
Great solution!
12/8/2011 2:12 AM | Duong
Gravatar

# re: Detecting the File Download Dialog In the Browser

I have been seeking this code for a long time! Great Solution!
Thank you very much

This idea is good for many things!
12/22/2011 12:14 PM | rspaz16
Gravatar

# re: Detecting the File Download Dialog In the Browser

What a great solution. Thanks.
1/14/2012 1:37 PM | Tom
Gravatar

# re: Detecting the File Download Dialog In the Browser

This is a brilliant solution, thanks!

One issue with it though in the example solution is the timer keep's running/doing the comparison. So you keep calling the finishDownload() over and over which might not be desirable (in my case, on subsequent 'downloads' my submit button which I was disabling until the file was generated kept getting enabled straight away).

Easily solved by clearing the timer (I used http://stackoverflow.com/a/2133217).

:)
2/22/2012 9:45 AM | Randeep Singh
Gravatar

# re: Detecting the File Download Dialog In the Browser

this solutions rocks. i use it with php. great!
3/8/2012 11:10 AM | Micha von Vau
Gravatar

# re: Detecting the File Download Dialog In the Browser

Thanks! This is exactly what I was trying to do, but then didn't differently and now changed it to this better version.
3/9/2012 2:08 AM | Chris
Gravatar

# re: Detecting the File Download Dialog In the Browser

Excellent ! Still useful these days !
4/18/2012 4:35 AM | none
Gravatar

# re: Detecting the File Download Dialog In the Browser

This solution is perfect! Works great with JSP. Thanks a lot!
4/28/2012 5:43 AM | Kevin
Gravatar

# re: Detecting the File Download Dialog In the Browser

Great article. works perfectly in asp.mvc 3
5/9/2012 2:19 PM | suresh
Gravatar

# re: Detecting the File Download Dialog In the Browser

Sorry, but it's doesn't work for me. I need to detect when a file finish download inside a IFRAME.
Always when I read the cookie with jquery, its value is NULL
This is my java code:

response.addCookie(new Cookie("tokenDocumentLoad", token));
response.setContentType("application/vnd.ms-excel");
response.setHeader("Cache-Control","no-store");

response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xls");
response.addHeader("Pragma", "public");
response.addHeader("Cache-Control", "max-age=0");
response.setDateHeader("Expires", (System.currentTimeMillis() + new Integer(getServletContext().getInitParameter("webAppResponseExpire")).intValue()));
ServletOutputStream servletOutputStream = response.getOutputStream();

fileContent....

servletOutputStream.flush();
servletOutputStream.close();
-------------------
This is the javascript code:

var cookieValue = $.cookie('tokenDocumentLoad');
alert("cookieValue:" + cookieValue);

5/18/2012 12:00 PM | dobrobuz
Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification:
 
 

Powered by: