jquery.fileDownload.js Library
jQuery File Download is a cross server platform compatible jQuery plugin that allows for an Ajax-like file download experience that isn’t normally possible using the web.
- Demo of jquery.fileDownload.js in action with some different examples
- Example VS2010 MVC 3 application using jquery.fileDownload.js
- GitHub – Send me a pull request!
- Download jquery.fileDownload.js v1.2.0 – Changelog
- You’ll need jQuery 1.3+ installed.
- Fully tested on:
- Internet Explorer 6 – 9
- Firefox 11 – reasonably sure it will work on earlier versions
- Chrome 17 – reasonably sure it will work on earlier versions
Features
- Brings control back to the developer by exposing a “successCallback” and “failCallback” that allows the developer to control the user experience in either situation.
- In conjunction with jQuery UI a developer can easily show a modal telling the user that a file download is occurring, disband the modal after the download starts or even inform the user in a friendly manner that an error has occurred. See the Demo for an example of this.
- File downloads don’t occur in the current window’s location bar. This means if a failure or something unexpected happens the user doesn’t leave the current page they are on or even have to see an ugly error message. This is critical for a good user experience especially on a highly Ajax application.
A classic problem with browser file downloads – Error handling
In a general case a file downloads occur after a user clicks an <a href=”location“> link. The href in the instructs the browser to browse to the location indicated. This is equivalent to setting the window.location of a page using JavaScript.
When the response comes back the HTTP response headers can contain many different things (in fact almost anything). Here are some examples:
Normal, HTML Response
|
1 2 3 4 5 |
Content-Type: text/html; charset=utf-8 |
The above “Content-Type” indicates to the browser that it should parse the DOM in the response and display it to the user. The location in the user’s location bar changes to reflect the address of the just-downloaded content.
Normal, “File Download” Response
|
1 2 3 4 5 |
Content-Disposition: attachment; filename=Report0.pdf |
The above “Content-Disposition” indicates to the browser that the server is trying to invoke a file download in the browser. The browser responds by opening up a file download dialog (or ribbon) for the user to download the file. As a convenience when the browser detects a response like this it doesn’t change the address bar so effectively the user stays on the same page.
Failed, “File Download” Response
|
1 2 3 4 5 |
Content-Type: text/html; charset=utf-8 |
As you may have guessed from that ugly highlighter color we’ve got trouble here. The response from a file download error is generally no different from a normal HTML response, the only difference here is that is has an error message as HTML content. The browser will now happily replace your existing page and address with the new error message. Not only have we now displayed an ugly error message to the user but we have also caused them to leave whatever page they were on.
Imagine you have created a nearly exclusively Ajax site like Gmail. A response like this from the server will cause your entire DOM to be replaced by an error message. Imagine this happening to you in Gmail and having to load up everything all over again. Ouch! My current role creating the framework for a highly Ajax application (like Gmail) inspired me to write this plugin for all to use – “web applications” is where the web is going anyways so there has to be a better way…
Another classic problem with browser file downloads – “Happy path” user experience
I probably just caught you thinking this: “well so what… my site never has any problems that cause error messages”… fair enough, but consider this:
- What is the response time of your web site in terms of serving up static files? Is it instantaneous? Is a user going to immediately look in the far corners of their screen for some sort of a spinning indicator or might they get confused or angry based on their technical prowess? Maybe they are looking at one of these? http://www.ict.griffith.edu.au/images/Animation/netscape_anim.gif (I hope not or you’ve got bigger issues)
- What if you are serving up a dynamically generated file (perhaps a report of SQL data?) based on user input that may take a few seconds. An average user might expect some sort of indication of what is going on. At the very least from a developer’s perspective it’d be nice if it wasn’t easy for them to hammer the download link a few times (to make it faster of course) wasting cycles across an entire n-tiered application.
I’ve got a solution… just use Ajax to download it!
Good idea! Unfortunately this is not possible due to one key problem:
- JavaScript, by design, doesn’t have the ability to perform lower level tasks on a users computer out of security concerns. Initiating file download prompts is one of these limitations.
You can certainly use an XMLHttpRequest object to download a binary (or otherwise) file but there is nothing you can do to the response to somehow get it saved on the user’s computer. Flash doesn’t have this limitation, but we really don’t want to get into Flash do we?
Enter jQuery File Download
jQuery File Download overcomes all of the aforementioned limitations of a “normal” browser file downloads. Well how the heck does that work? The concept is nothing new in fact: If you browse around the web you will find plenty of forum posts discussing the same technique, I just wasn’t able to find a succinct easy to use plug-in to do it hence my decision to create this plugin. The answer is:
An iframe and cookie
What?! I have to rely on Web .1 technology to make my Web 2.0 Ajax application user friendly? Turns out this is the magic bullet combination to work around normal limitations of file downloads, here’s how:
iframe
An iframe (which is generally a mortal sin of modern web development) can exist in a DOM but in most respects is treated like an entirely different window. By dynamically inserting a hidden iframe into the DOM and setting its location to the desired file path we can initiate a file download just like it was occurring in the main application window. This directly gets us around one of the nasties of file downloads – if an error occurs the user has now been forced off of the page they were on (which may contain an Ajax application like Gmail) to see an ugly error message.
Like a normal file download in the main window an iframe will never fill with content when a successful file download occurs. It simply contains an empty DOM. Well how do we detect what is going on if nothing happens when the file download is successful. Well this is where the cookie comes in:
cookie + iframe
Since the creation of cookies exists in HTTP headers, which is standard fare for all web requests and responses, we can actually write a cookie to indicate that a file download has been initiated properly (instead of an error page). The response from the web server will now look something like this:
|
1 2 3 4 5 6 |
Content-Disposition: attachment; filename=Report0.pdf Set-Cookie: fileDownload=true; path=/ |
While we can’t directly tell if a file download has occurred we can check for the existence of a cookie which is exactly how jQuery File Download works. Once a download is initiated on the iframe a configurable duration poll of cookies and the iframe contents occurs. If the iframe fills with contents then we know a file download has occurred (in most cases, make sure to set a MIME type!). If the cookie gets written then we know to stop polling and kill the iframe because the file download dialog/ribbon has been displayed.
Using jQuery File Download – JavaScript
jQuery File Download is easy to use in the simple case but also allows for various callback options as well. Don’t forget to add the required cookie code in the next section!
Very simple code demo
|
1 2 3 4 5 |
$.fileDownload('/url/to/download.pdf'); |
Use of the very simple approach only protects you from the page changing in the event of a file download error. This alone is pretty useful. If an error occurs the user gets an alert() dialog that says “A file download error has occurred, please try again.” You can see this demoed here under the “Barebones – jquery.fileDownload.js” heading. I wouldn’t recommend using this option given a much better experience is only a few more lines away…
Impractical code demo of commonly used features
This just demos what you could do but I’d highly recommend against it unless you want confused and annoyed users. This will result in an alert() dialog telling you a file download initiation has just occurred for the specified URL. If a failure occurred it will display the attempted URL and error message in a dialog.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$.fileDownload('/url/to/download.pdf', { successCallback: function (url) { alert('You just got a file download dialog or ribbon for this URL :' + url); }, failCallback: function (html, url) { alert('Your file download just failed for this URL:' + url + '\r\n' + 'Here was the resulting error HTML: \r\n' + html ); } }); |
Simple rich user experience code demo
You can see this one running at the top of the demo page. In order for the below code to work for you, you will also need jQuery UI installed and an image (if you want) as a downloading spinner.
This code will cause any <a> with class=”fileDownload” like below
|
1 2 3 4 5 |
<a href="/mvcdemo/FileDownload/DownloadReport/0" class="fileDownloadSimpleRichExperience">Report0.pdf</a> |
that is ever loaded into your site (assuming a globally loaded JavaScript reference) to initiate a “rich user experience”:
- User is informed of a pending report download via a jQuery UI modal that says “We are preparing your report, please wait…”
- Success:
- Modal goes away just as the browser’s file download dialog/ribbon occurs
- Failure:
- User is informed of an error in a jQuery UI modal: “There was a problem generating your report, please try again.”
Another plus to this approach is that it degrades gracefully in case the user doesn’t have JavaScript running because an a href certainly better work in any web browser out there!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//the below uses jQuery "on" http://api.jquery.com/on/ (jQuery 1.7 + required, otherwise use "delegate" or "live") so that any //<a class="fileDownload..."/> that is ever loaded into an Ajax site will automatically use jquery.fileDownload.js //if you are using "on": //you should generally be able to reduce the scope of the selector below "document" but it is used in this example so it //works for possible dynamic manipulation in the entire DOM // // Simple rich user experience - jquery.fileDownload.js & jQuery UI Dialog // uses the optional "options" argument // $(function() { $(document).on("click", "a.fileDownloadSimpleRichExperience", function() { $.fileDownload($(this).attr('href'), { preparingMessageHtml: "We are preparing your report, please wait...", failMessageHtml: "There was a problem generating your report, please try again." }); return false; //this is critical to stop the click event which will trigger a normal file download! }); }); |
Custom rich user experience code demo
This demo does almost the same thing as above but handles the modals manually by using the callbacks and it uses modal HTML that already exists on the page.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
//the below uses jQuery "on" http://api.jquery.com/on/ (jQuery 1.7 + required, otherwise use "delegate" or "live") so that any //<a class="fileDownload..."/> that is ever loaded into an Ajax site will automatically use jquery.fileDownload.js //if you are using "on": //you should generally be able to reduce the scope of the selector below "document" but it is used in this example so it //works for possible dynamic manipulation in the entire DOM // //Custom rich user experience - jquery.fileDownload.js & jQuery UI Dialog //uses the optional "options" argument // $(function() { $(document).on("click", "a.fileDownloadCustomRichExperience", function() { var $preparingFileModal = $("#preparing-file-modal"); $preparingFileModal.dialog({ modal: true }); $.fileDownload($(this).attr('href'), { successCallback: function(url) { $preparingFileModal.dialog('close'); }, failCallback: function(responseHtml, url) { $preparingFileModal.dialog('close'); $("#error-modal").dialog({ modal: true }); } }); return false; //this is critical to stop the click event which will trigger a normal file download! }); }); |
HTML for jQuery UI Modals (place anywhere on page)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<div id="preparing-file-modal" title="Preparing report..." style="display: none;"> We are preparing your report, please wait... <!--Throw what you'd like for a progress indicator below--> <div class="ui-progressbar-value ui-corner-left ui-corner-right" style="width: 100%; height:22px; margin-top: 20px;"></div> </div> <div id="error-modal" title="Error" style="display: none;"> There was a problem generating your report, please try again. </div> |
Using jQuery File Download – Server Code for Cookie
Only the MVC 3 server code has been tested. Actually if you want to see it running just head over to the demo page. Just make sure to write a cookie in the same HTTP response as the file download occurs with, that results in this HTTP Response Header (using default configuration):
|
1 2 3 4 5 |
Set-Cookie: fileDownload=true; path=/ |
ASP.NET MVC 3
If you are using a Controller Action to serve up your files (I hope you are!) you can very easily enable automatic cookie writing by inheriting your Controllers from a base class that writes the cookie automatically for you. This assumes that you are using MVC helpers for returning “FileResult”s (or derived classes like FileStreamResult). MVC FTW.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class FileDownloadController : Controller { protected override void OnResultExecuting(ResultExecutingContext context) { CheckAndHandleFileResult(context); base.OnResultExecuting(context); } private const string FILE_DOWNLOAD_COOKIE_NAME = "fileDownload"; /// <summary> /// If the current response is a FileResult (an MVC base class for files) then write a /// cookie to inform jquery.fileDownload that a successful file download has occured /// </summary> /// <param name="context"></param> private void CheckAndHandleFileResult(ResultExecutingContext context) { if (context.Result is FileResult) //jquery.fileDownload uses this cookie to determine that a file download has completed successfully Response.SetCookie(new HttpCookie(FILE_DOWNLOAD_COOKIE_NAME, "true") { Path = "/" }); else //ensure that the cookie is removed in case someone did a file download without using jquery.fileDownload if (Request.Cookies[FILE_DOWNLOAD_COOKIE_NAME] != null) Response.Cookies[FILE_DOWNLOAD_COOKIE_NAME].Expires = DateTime.Now.AddYears(-1); } } |
ASP.NET
Static Code
|
1 2 3 4 5 |
HttpContext.Current.Response.SetCookie(new HttpCookie("fileDownload", "true") { Path = "/" }); |
Response-aware code
|
1 2 3 4 5 |
Response.SetCookie(new HttpCookie("fileDownload", "true") { Path = "/" }); |
PHP (with example file)
|
1 2 3 4 5 6 7 8 |
header('Set-Cookie: fileDownload=true; path=/'); header('Cache-Control: max-age=60, must-revalidate'); header("Content-type: text/csv"); header('Content-Disposition: attachment; filename="'.$title.'-' . $timestamp . '.csv"'); |
That’s it!
Let me know if you have any issues or can think of some nice features to add.
Hi John
Have you tested this script in IE8?
The file download dialog does not show after the initial file generation (animation) completes. I don't personally have IE8 installed, but used IETester instead and is supposed to give accurate behavior.
Hi Ronnie,
Thanks for the feedback. Funny thing: I actually noticed the same behavior in IE Tester IE8 mode, the dialog didn't come up. I have fully tested in native IE 6-9 installs and everything appears to work properly in my demo.
IE Tester is a pretty cool thing for high level testing (I use it for starters on everything that I need cross IE browser support) but sometimes it gets a little weird with things so I'll always finish off testing in a native install. Too bad you can't run multiple IEs on a single box right…
I'm updating the post to include tested browsers. Let me know if you notice anything else or can think of some cool features to add!
John
First of all, great Plugin. Really usefull.
I tried in my webbrowser, working properly.
Does it work on webkit? I mean it does but actually I decided to include this into my phonegap app (using jQuery Mobile) and when i click the link it leaves the application and redirect to the native browser.
Did I miss something?
Hey Jeelig,
Thanks for the feedback. If you are using PhoneGap that actually makes a bit of sense since it probably sees the IFRAME come in (how I implemented this thing) and point at a URL. Without it knowing any better it probably just spawns up a browser to deal with that event since IFRAMEs are generally junk (except in this one case, ha!). Take a look at this forum post I found to help clarify:
http://groups.google.com/group/phonegap/browse_thread/thread/bd1280d14ff1ff81?pli=1
Unfortunately this is probably permanent so you may need to find a different solution (perhaps using native code in the device you are targeting even?) Let me know if/what you use to get this figured out!
John
Hi There!
First of all, great Plugin!
But I'm having some troubles trying to use it. Since it never stops, this line
if (document.cookie.indexOf(settings.cookieName + "=" + settings.cookieValue) != -1)
Is never true, so it doesn't stop, and no success function is fired. It happens on last versions of FF, Chrome and safary.
Any clue?
Hi Emilliano,
Are you writing a cookie along with your file download? Unfortunately this is the only way to detect file downloads reliably cross browser. Make sure (using FireBug, Chrome, Fiddler etc.) that your response headers along with the file download contains "Set-Cookie: fileDownload=true; path=/" or else there is no way for jQuery File Download to know that a file download just occurred. I hope this helps!
John
Hi John,
I have it working!
Thanks a lot! Great tool!
Hi John,
1st of all, very interesting your feature. I am having trouble to use with PHP.
My Chrome is returning. Do you know why?
Uncaught TypeError: Object function (a,b){return new e.fn.init(a,b,h)} has no method 'fileDownload'
Hi Ricardo,
I'm not sure what could cause that issue without doing local debugging of it. Make sure that you have added a script reference to jQuery before your script reference to jQuery file download. Also try calling jQuery file download with the exact same syntax as mentioned in one of my posts to make sure its not a syntax issue (for starters). Hope you get it figured out
Not sure if I've done this right. I have a gaming site and i'm trying to use your script to give the .exe installing apps a better experience for all the reasons you noted. I've included your modal dialog version of the script. Demo here http://www.stimulus.com/my/cTest.php. What I'm not sure is about the server side cookie. I use a redirect script to track all the download requests in PHP. Prior to setting the header location, I'm setting the cookie to your PHP line above. What happens on IE is that the download script reports a failure, but successfully executes the request. On the Mac (which is not really needed at this moment, but will be later) it flashes the processing dialog and doesn't execute the download at all.
My question is, do I need to set the cookie AFTER the file download has queued or finished? I would have to write a proxy streaming script to know when its done since I'm linking outside my site. Not a big deal, but not something I saw noted in your example.
Hi DrTyrell,
I took a look at the site indicated and it doesn't appear to be running anymore. The cookie should be written along with the response stream that contains the file. In my examples it looks something like "Content-Disposition: attachment; filename=Report0.pdf" to send the file along with the file bytes. The cookie needs to be included in a header with that. Hopefully this helps!
John
There is a issue for Firefox, if my FF prompts me selecting the path to download, and at that time, the temp iframe has already been removed, so the download will not continue, anyway can detect the save as dialog?
Plugin is unable to download 'dat' and 'jpg' files. I am not sure whats going wrong? Your comments would be appreciated. Thanks.
Hello John
I have used the same code that you have given but in simple ASP.Net , What should be the URL Passed in the link , I am giving the path of the pdf, But it is going to the Failure , what could be the reason, I will be really great full if you can help me on this.
Anuj
For php you have to return the cookie in the header information.
If you try using setcookie("fileDownload",”true”, 0, "/"); you will get a successful download but the UI dialog box will not close.
example.
header('Set-Cookie: fileDownload=true');
header('Cache-Control: max-age=60, must-revalidate');
header("Content-type: text/csv");
header('Content-Disposition: attachment; filename="'.$title.'-' . $timestamp . '.csv"');
Thanks Jared, I'll update my example code for PHP
Nice, I like it. Provided a solution to a large file download problem I was having.
I can't help but think though, is the cookie component even necessary? The iframe state can be tested using .readyState, ie:
if (formDoc.readyState == "complete") {
//execute specified callback
internalCallbacks.onSuccess(fileUrl);
cleanUp();
see the below article:
http://stackoverflow.com/questions/6183737/dynamically-create-an-iframe-and-attach-onload-event-to-it
Am I missing something here? Cross browser compatibility perhaps?
My successCallback is never called because
if (document.cookie.indexOf(settings.cookieName + "=" + settings.cookieValue) != -1)
is always false
On the server side I added
header('Set-Cookie: fileDownload=true');
into my PHP with other headers
I also looked up wireshark and I am sure the Set-Cookie is in the header
Somehow my browser does not write Set-Cookie in to browser's cookie
Any idea?
Thanks~
Hi Stumblor,
Good idea on checking the iframe's readyState. Your right though, unfortunately when I was testing this approach Chrome didn't work. Each browser that did work had different things happen to the readyState (of course right?) but at least it changed, Chrome's simply wasn't affected at all. Thanks for the thought!
John
Howdy,
Just wondering if anyone else has noticed an intermittent failure with this plugin when using Firefox? This is with a "straight" out of the box Firefox install (v13.0.1, in case anyone is interested), and no extensions added.
I'm seeing this *only* on Firefox. Chrome, Opera, Safari and, yes, even IE(!) work just fine.
It's easily reproducible for me on the demo page (http://jqueryfiledownload.apphb.com/). 1 in about every 8 clicks on the top "Report0.pdf" will fail silently – that is, the "We are preparing your report, please wait…" dialog disappears but no file save download dialog appears. There is absolutely no indication that anything has gone wrong apart form this.
When the failure occurs, it's not permanent, and clicking on the link again usually gets me the file (but it still fails intermittently afterwards).
I've tried to figure out what's going on by installing the FireBug extension and debugging, but I can't track down the difference between the successes and failures. The flow of execution seems the same in both cases. Having said that, I have noticed that:
a) the presence of the FireBug add-on *seems* to increase the number of failures
b) if I remove the call to kick off the checkFileDownloadComplete on line 286 of v1.3.0, the problem *seems* to go away (but then, of course, the success/fail callbacks never happen)
c) similar to (b), if I increase the checkInterval beyond the length of time the file takes to prepare on the back end, the problem seems to go away. For example, if my test file takes 5 seconds to prepare, and I set the checkInterval to 10000 (i.e., 10 seconds), then all is good.
b) and c) indicate a problem with the checkFileDownloadComplete() method, but it all looks very innocent to me.
Also, it could just be that I have been lucky and not seen it under those conditions – the usual problem with the intermittent problems…
It'd be useful to know if anyone else has noticed this issue!
Apart from this, the plugin is very clever and most awesome – great work!
Cheers!
Andrew
Hello John.
This tool is really useful. I have it working but it would seem that it doesn't work for ALL extensions of files. At least that's my conclusion after trying to download 5 files with the same name but different extensions. Some were downloaded but to the others appeared the "A file downloaded error …" box and didn't download. And it's happening a funny thing in IE. When I request the download, the save box shows up but also the error box (when downloading known extensions).
Apart form that, nice work!
Good plugin, i have a question.
its avaible without jqueryUI???
hey john, thanks so much for jQuery filedownload, it's great.
i'm stuck on a problem with it and hope you can help, wasn't sure if a message or a post would be better.
my files are stored in s3 buckets.
- i generate the s3 auth url on my app server and then pass that url to $.fileDownload().
- app server sets the cookie fileDownload=true when it returns that generated s3 url
- i have s3 returning the correct content-disposition: attachment header to force download.
but no file download is initiated.
if i hit that s3 generated link in a new tab, it downloads correctly. if i try $.fileDownload() on a file that is served directly from my app server (where i set-cookie fileDownload=true) the download is initiated correctly.
i can't set-cookie from the amazon s3 server. does that mean i'm out of luck? or is there another way to approach this? does the set-cookie header have to happen in the same response that serves the file
i forgot to add that the download does succeed, the onSuccess callback happens and watching traffic in my http proxy i see that the file does get transferred. the problem is that no file dialog appears to save as, nor does the file download appear in the downloads section of the browser, it seems to just get loaded into the current document invisibly.
..and here i am again. upon further debugging, i understand the issue here. since i'm making two requests, first to my app server for the generated s3 url, second to s3 using that generated url, the iframe disappears. here's why:
on the first call to the app server, set-cookie downloadFile=true happens, which checkFileDownloadComplete sees and kills the iframe before it can start downloading from s3.
but you can't set cookies from s3, so is there a solution here? i'm new to javascript and jquery so i'm not sure. if i don't ever set that cookie, i can use the basic functionality of this plugin, but i would like to use the advanced functionality if i can.
Hi John,
Your lib accepts a array in [b]'data'[/b] parameters?
Example:
A checklist sends a lot of params with the same name, so the server is waiting a list like object.
Thanks! Great work!
If you are using .Net, beware of the <httpCookies httpOnlyCookies="true" requireSSL="false" /> setting. If you have httpOnlyCookies on you won't be able to read the cookie client side and it won't work. I think this might even be the default option.
[HttpPost]
public FilePathResult DownloadReportPost(int foo)
{
//How do we get values from the entire page here
string s = "are you getting page contents here?";
return GetReport(foo);
}
In the above, how do I get all values on the form on client (HTML page)?? thanks.
Lakpak Sher
This is in response to Andrew Laws and anyone else experiencing intermittent failures to get prompted to save a file. The problem appears to be due to the iframe getting removed too quickly. I solved the problem by changing line 369 of v1.3.0:
setTimeout($iframe.remove, 1000);
The added delay seems to clear up the problem. A better solution might be to check and see if the iframe already exist when it is being created and if so, remove the existing one first.
I need to use this plugin with coldfusion 9. Here I found demo with ASP.NET & PHP. I want to use this with coldfusion. No idea how to manage code related to cookie as shown for ASP.NET. Any idea?!
I have an odd dilemma. This is using Firefox 15.0 (release).
If I do not add the SetCookie to the header, my callbacks are called too early.
If I DO add the SetCookie to the header, it may be 2 or 3 clicks before I see the dialog asking whether I want to open or save the file, but the file save dialog never appears after I click "Ok" on that one. However, the callbacks don't get called until that dialog goes away, which is what I'd expect.
Any ideas?
I confirm that for firefox there is problem. It'll fail silently and randomly even on demo for success case.
Hi Everyone,
Thanks for the heads up on the Firefox issue and the solution Andrew Laws/Mark. I have updated the GitHub and the demo site with a setTimeout 0 around the cleanup which should clear the stack before cleaning up hopefully fixing the issue. I'm unable to get it to happen now. Anyone please let me know if you can get it to happen again, thanks!
John
Cheers for that John!
As far as I can tell, your changes have fixed the problem.
Thanks. This plugin saved a lot of time.
Hi John
After you've added "var $ = jQuery.noConflict();" to the script and after I implemented System.Web.Optimization for bundling and minification in MVC4, the script will no longer work in IE8.
The error "'$' is null or not an object" is thrown and then of course numerous "object expected" errors.
If I remove uncomment that line, everything loads fine. The line causes no problem in other webbrowsers, that I have tested with (IE9, IE10, Firefox, Chrome).
@Ronnie
Interesting, thanks for bringing this up.
Fix one bug cause another, welcome to software development eh?
Any ideas what might be causing this?
Very interesting plugin and it works for me except for IE7. Trouble in my case is, that the download may be empty, in which case failCallback is used. In IE7, however, this is not enough. After a lot of trial/errors I managed to copy your examples completely and found out that with error code 500 for empty response it works OK in all my browsers.
But the question is – is there any other way how to treat empty download differently? What if real error occured on the server – then I can't distinguish it from empty response (which I have to mask as 500). I originally tried 404, which was probably deep misunderstanding on my part – this rather refreshed the whole page in the browser.
I'd welcome any suggestions as I'd love to use this plugin in our future works, it seems quite reliable when I stick to 500 code for errors
and the experience is just great. Users just can't be talked out of wanting to disable buttons and then enable them again, so we have to use something like this.
@Richard:
Thanks for the feedback. I have noticed things getting hairy with IE due to the "stock" pages that it presents with certain >= 500 HTTP Response codes. I'm not aware of any way around it at this point unfortunately.
John, I managed after all. I can recognize empty download on server side, so I sent the same cookie but with different value. Then I copied your check of a cookie and callbacks and made emptyCallback available. It is pleasure to work with your jQuery plugin and it was easy to adapt it – even for JS/jQuery newbie like I am.
Now I have success, empty download and error as three different paths with three different feedbacks for user. Thank you again very much.
Hello John,
I am having problems with getting the dialog to close. Files are successfully downloaded, but the dialog remains open after the download. I am using the code snippet from your "Custom rich user experience" that employs the 'preparing-file-modal' divs. The cookie is correctly returned in the response header, so I don't believe that is the problem. Also, the text in the dialog "We are preparing your report, please wait…." does not appear. This problem occurs in both Firefox 15.0.1 and Chrome. Any clues on what I should be checking would be appreciated.
Thanks much, Bill
Hello John,
I am having a few problems using the code from your "Custom rich user experience – jquery.fileDownload.js & jQuery UI Dialog". The modal dialog opens and the file download completes, but the dialog box does not close. It is not clear that the successCallBack function is ever called. The response header for the downloaded file has the appropriate settings for the cookie, as you describe in the documentation, so I'm not sure what might be broken. Also, the text "We are preparing your report, please wait…" never appears in the dialog. This behavior is true for both Firefox 15.0.1 and Chrome.
Thanks, Bill
@Bill Hudspeth
I would try stepping through the jQueryFileDownload source and see why the callback isn't getting called. I don't think it's an issue with the code since it appears to be working in the demo but you never know. If I were to throw a guess out there I would think that perhaps the cookie is being written incorrectly. For example are you writing for path=/?
Hello John,
Looks really nice! I've been looking for a pluging like this for a while.
Anyway, short question. Are there any plans to have the filedownload function return an appropriate promise/deferred object? I checked the source code, but couldn't find anything.
@Sumadartson
Glad the plugin is working for you. That's a great idea to include the promise/deferred interface. Perhaps I can include that in a future commit. Feel free to contribute on GitHub in the mean time if I don't get to it for awhile. Thanks!
I'm having problems setting the cookie from a WebAPI controller. Anyone have an idea on how to accomplish this?
Can this plugin send json data via post, to the server?
Hi John,
Using version 1.3.3 I noticed the following browsers behaviors : IE9 (9.0.8112.16421) and Safari 5.1.7 don't properly handle the file download when http method is POST (I need to use POST rather than GET because I may send a lot of data from page): file downloads but there is no prompt for user to do something with file data (save, open…). I searched on the internet and send you a diff file (I don't have a github account, sorry) for the following changes : add a name for iframe, target form to it. For both browsers I quoted above, rather set target to _blank.
I saw that non-hidden iframe will show a PDF document (the only kind of file I tested) for IE9 if targeting form to it. But its default size is unpractical. For browsers which prompt user, non-hidden frame border is visible, and it just blinks on file download, right before prompt displays.
Finally I wonder whether I've found something specific to PDF files, related to PDF preview in browser plugin being installed ? Thank you for your feeback about this and the patch I propose.
Thank you for this great plugin,
— eaa_jquery.fileDownload-1_3_3.js 2012-10-15 10:33:31.711876000 +0200
+++ eaa_jquery.fileDownload.js 2012-10-16 11:37:09.265816700 +0200
@@ -115,8 +115,12 @@
var isIos = false; //has full support of features in iOS 4.0+, uses a new window to accomplish this.
var isAndroid = false; //has full support of GET features in 4.0+ by using a new window. POST will resort to a POST on the current window.
var isOtherMobileBrowser = false; //there is no way to reliably guess here so all other mobile devices will GET and POST to the current window.
+ var isTargetNewWindows = false; //there are browsers like some versions of IE9, Safari 5, which don't open user prompt after file download with POST method to iframe, so use blank target.
- if (/ip(ad|hone|od)/.test(userAgent)) {
+ if (userAgent.indexOf('trident/5.0') != -1 || userAgent.indexOf('safari/') != -1) {
+ isTargetNewWindows = true;
+ }
+ else if (/ip(ad|hone|od)/.test(userAgent)) {
isIos = true;
@@ -127,7 +131,6 @@
} else {
isOtherMobileBrowser = /avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|playbook|silk|iemobile|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(userAgent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i.test(userAgent.substr(0, 4));
-
}
var httpMethodUpper = settings.httpMethod.toUpperCase();
@@ -270,32 +273,28 @@
.html(formInnerHtml);
} else {
-
+ // POST method
if (isIos) {
downloadWindow = window.open("about:blank");
downloadWindow.document.title = settings.popupWindowTitle;
formDoc = downloadWindow.document;
window.focus();
-
} else {
-
- $iframe = $("<iframe style='display: none' src='about:blank'></iframe>").appendTo("body");
+ $iframe = $("<iframe style='display: none' src='about:blank' name='my_iframe'></iframe>").appendTo("body");
formDoc = getiframeDocument($iframe);
}
-
- formDoc.write("<html><head></head><body><form method='" + settings.httpMethod + "' action='" + fileUrl + "'>" + formInnerHtml + "</form>" + settings.popupWindowTitle + "</body></html>");
+ var myTarget = isTargetNewWindows ? "_blank" : 'my_iframe';
+ formDoc.write("<html><head></head><body><form method='" + settings.httpMethod + "' action='" + fileUrl + "' target='" + myTarget + "'>" + formInnerHtml + "</form>" + settings.popupWindowTitle + "</body></html>");
$form = $(formDoc).find('form');
}
$form.submit();
}
-
//check if the file download has completed every checkInterval ms
setTimeout(checkFileDownloadComplete, settings.checkInterval);
-
function checkFileDownloadComplete() {
//has the cookie been written due to a file download occuring?
@@ -375,7 +374,7 @@
function cleanUp(isFailure) {
- setTimeout(function() {
+ setTimeout(function () {
if (downloadWindow) {
Hi John,
great plugin, and indeed very useful.
Tested v1.3.3 on OSX 10.7.5 – FF 16.0.2 and Safari 6.0.1.
I tried to throw an error response for the form example (in my case, the php file echoes some html as a warning for no data to be downloaded):
- OK in FF
- in Safari it looks like the try/catch part in checkFileDownloadComplete() (line #318) is never reached, being always intercepted by the preceding check for cookie existence (line #302).
Switching the order of the two statements everything works fine.
Is it me or anybody else had the same behavior?
Thanks, Gianni
This plugin does not work if Content-Disposition: inline; is returned. pdf file is not opened in new tab. Nothing happens. How to open pdf file in new tab using this?
I implemented the code in vb.net but it does not work.No error in show but it just does not download.
code used in server side
HttpContext.Current.Response.SetCookie(New HttpCookie("fileDownload", "true") With { _
.Path = "/" _
})
——————————————————-
Javascript to fire on onclick
function downfile()
{
try
{
//alert("entred");
var linkHref = "http://anywebsite.com/images/1.jpg";
$.fileDownload(linkHref);
}
catch(e)
{
}
}
Can any body guide me with this.
I have problem integrating the code with VB.net can somebody help with a demo code in vb.net.
Hi John,
First of all thank you for the wonderful script. Saved a lot of time in exporting the file to Microsoft Excel and download the same from the server.
Though I could force download the file which was opening the file dialog, I was looking at an option where the download success message was displayed on the user screen. You script help me do it. I did have a small glitch in the code. In your example for PHP you have set the cookie name to be 'fileDownload' and its value to 'true'. For some reason the condition where the cookies are checked for equality failed (ver 1.3.3 line # 304). So I got the PHPSESSID cookie value and set the same for the options in the script. Now the onSuccess call back got a hit. But unfortunately, the cookie was being removed and this used to expire my session. I commented out the cookie removal code and its working fine. I am yet to test further to see if there is any issues in commenting out the code in your script. Looking forward to your suggestions and any corrections to the approach I adopted.
Thanks,
Girish
Hi John,
First of all thank you for the wonderful script. Saved a lot of time in exporting the file to Microsoft Excel and download the same from the server.
Though I could force download the file which was opening the file dialog, I was looking at an option where the download success message was displayed on the user screen. You script help me do it. I did have a small glitch in the code. In your example for PHP you have set the cookie name to be 'fileDownload' and its value to 'true'. For some reason the condition where the cookies are checked for equality failed (ver 1.3.3 line # 304). So I got the PHPSESSID cookie value and set the same for the options in the script. Now the onSuccess call back got a hit. But unfortunately, the cookie was being removed and this used to expire my session. I commented out the cookie removal code and its working fine. I am yet to test further to see if there is any issues in commenting out the code in your script. Looking forward to your suggestions and any corrections to the approach I adopted.
Thanks,
Girish
Great plugin, John! I had some problems getting successCallback to fire, but found the problem after a little while. You can save future PHPers a little confusion if you change your PHP example as follows:
header('Set-Cookie: fileDownload=true'); // no workie
to
header('Set-Cookie: fileDownload=true; path=/'); // no workie
Thanks for the plugin! I'm able to get it working on all browsers. But in iOS, if popup is enabled, I can not get the new tab to load. Any way to bypass that?
Hello;
Great plugin this is amazing! I do have a question although I think I know the answer I thought I'd ask anyways. In your example using MVC you return the value of FilePathResult to the method. Is it possible to use the HttpResponseMessage and actually return a file that's in memory? The case I have is that I'm generating the file on the fly and trying to download it using your amazing plugin. Any help would be great! Thank you very much.
Hi
Great plugin! I do have a question though: I want to perform various other ajax operations while the download is in progress (my downloaded file takes a while to create on the server). On the server I keep track of how much progress the 'file building' has made. After my call to $.fileDownload(…) I want to regularly call another ajax.get method which will let me update a progress bar with accurate values.
At the moment (unless I've configured it wrong), any ajax calls I make after $.fileDownload(…) are queued up and only get executed after the file download has completed. I've even tried adding a $.get(…) call inside the 'checkFileDownloadComplete' function, but it still gets queued.
Is there any way to achieve this functionality?
Thanks!
Hi,
first of all thanx for this plugin, which is very useful indeed…
I'm having though an issue when downloading a pdf on iPad in full screen mode.
Doing a little research on google, it turns out that window.open on a full screen web app doesn't work,
and should be substituted with the normal href action of the link.
Is there a way to get this plugin working for an iOS web app working in full screen mode?
Thanks,
Simone
I have a problem retrieving files address with folder.
For instance, ./invoice/invocie_1.pdf became ._invoice_invocie_1.pdf
anyone knows why ?
Thx
- Kiko -
Hi John
I am trying to use the plugin for a DotNetNuke site, I need to allow user to download a PDF which is already created at path somewhere at website/desktopmodules/download/file/path/of/file.pdf but at line 370 of plugin it is saying "Permission denied to access property 'body'" …what is this…i am stuck on this feature and need it urgently as it is really a great plugin to use. jquery code i have used is as below :
$.fileDownload(r.d, { preparingMessageHtml: "We are preparing your report, please wait…",
failCallback: function(html, url) {
alert('Your file download just failed for this URL:' + url + '\r\n' +
'Here was the resulting error HTML: \r\n' + html);
}
});
Where r.d is a nested path to pdf. Please suggest on what step I am wrong. Thanks for the plugin
Could you please tell How we can set the responseHtml(used in fail callabck) from server, from where we are getting this value
Hi All,
Download processing is fine but after downloading the file dialog is not closing.I have read the comments and i have come to know the problem is that need to add setCookie method but where i have to place this bit of code. i haven't get an idea.Now am working on Spring MVC so could any one please help me ASAP.I will be greatly if help. Thank you in advance.
Is it possible to add request headers to the download request or does everything have to be in the URL as query string parameters? Since this is basically driving an IFRAME, my suspicion is that it's not possible to augment the request in this manner, but it certainly doesn't hurt to ask!
Could you create and publish a nuget package for the visual studio developer ?
Excellent idea! I’ll see if I can get this done soon…