Download a file by jQuery.Ajax

in Firebug I see the data is retrieved with the Binary stream. I wonder how to open the file downloading window with which the user can save the file locally?

38.4k 26 26 gold badges 201 201 silver badges 275 275 bronze badges asked Dec 28, 2010 at 10:21 35.8k 54 54 gold badges 167 167 silver badges 300 300 bronze badges possible duplicate of How to download a file on clicking the name of file using PHP? Commented Dec 28, 2010 at 10:29

I marked it as a duplicate despite the platform difference, because as far as I can see the solution is the same (You can't and don't need to do this through Ajax).

Commented Dec 28, 2010 at 10:30 so,without ajax,just use the window.location="download.action?para1=value1. "? Commented Dec 28, 2010 at 10:32

none of the answer is good and easy to read, all of them require a freaking hard tweak. 12 years of frontend engineering and still isnt a builtin function for this matter is amazing

Commented Oct 24, 2023 at 10:17

26 Answers 26

2019 modern browsers update

This is the approach I'd now recommend with a few caveats:

fetch('https://jsonplaceholder.typicode.com/todos/1') .then(resp => resp.blob()) .then(blob => < const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.style.display = 'none'; a.href = url; // the filename you want a.download = 'todo-1.json'; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); alert('your file has downloaded!'); // or you know, something with better UX. >) .catch(() => alert('oh no!'));

2012 Original jQuery/iframe/Cookie based approach

Bluish is completely right about this, you can't do it through Ajax because JavaScript cannot save files directly to a user's computer (out of security concerns). Unfortunately pointing the main window's URL at your file download means you have little control over what the user experience is when a file download occurs.

I created jQuery File Download which allows for an "Ajax like" experience with file downloads complete with OnSuccess and OnFailure callbacks to provide for a better user experience. Take a look at my blog post on the common problem that the plugin solves and some ways to use it and also a demo of jQuery File Download in action. Here is the source

Here is a simple use case demo using the plugin source with promises.

$.fileDownload('some/file.pdf') .done(function () < alert('File download a success!'); >) .fail(function () < alert('File download failed!'); >); 

Depending on what browsers you need to support you may be able to use https://github.com/eligrey/FileSaver.js/ which allows more explicit control than the IFRAME method jQuery File Download uses.

answered Apr 2, 2012 at 3:48 John Culviner John Culviner 22.8k 6 6 gold badges 57 57 silver badges 52 52 bronze badges

I love what you built but I suspect that to get more StackOverFlow credit your answer here should contain a bit more detail. Specifically on how you solved the problem.

Commented Aug 7, 2012 at 21:58

it would be nice if you would mention exactly how this "plugin" gets around the limitation, rather than forcing us to go to your blog/plugin source to see it. for example, is it instead posting to an iframe? is it instead requiring the remote script to save the file and return a url to it?

Commented Sep 23, 2013 at 19:47 @asgerhallas Sure, but that's completely useless if said link goes away. Commented Oct 29, 2013 at 14:06

I agree, a blog is a far better place to place a lengthy description of how to use your plugin and how it works. but you could have at least gave a short overview of how this plugin solves the problem. For example, this solves the problem by having the server set a cookie and having your javascript continuously look for the cookie until it exists. Once it exists, we can assume that the download is complete. With that kind of information one could easily roll their own solution very quickly, and the answer no longer relies 100% on your blog/plugin/jquery and can be applied to other libraries.

Commented Oct 30, 2013 at 20:10

Royi, as I understand it AJAX can never support file downloads that result in a file download popup to save to disk. Have you found a way that I'm unaware of?

Commented Jan 13, 2014 at 16:19

Noone posted this @Pekka's solution. so I'll post it. It can help someone.

You don't need to do this through Ajax. Just use

window.location="download.action?para1=value1. " 
answered Oct 5, 2011 at 11:50 27.1k 28 28 gold badges 124 124 silver badges 183 183 bronze badges

Nice one. as I was struggling with handling the download file prompt and using jquery ajax..and this solution works perfectly for me ..+1

Commented Mar 1, 2013 at 8:49

Note that this requires the server to be setting a Content-Disposition header value of 'attachment', otherwise the browser will redirect to (and display) the response content

Commented Apr 24, 2013 at 21:45

Or alternatively use window.open(, '_blank'); to ensure that the download won't replace your current browser content (regardless of the Content-Disposition header).

Commented Aug 15, 2014 at 15:38

The problem with this solution is that if the operation fails/the server returns an error, your page will be redirected to the error page. To solve that use the iFrame solution

Commented Jun 30, 2015 at 23:52 The real problem with this solution - question is about POST request. Commented May 28, 2019 at 3:45

You can with HTML5

NB: The file data returned MUST be base64 encoded because you cannot JSON encode binary data

In my AJAX response I have a data structure that looks like this:

That means that I can do the following to save a file via AJAX

var a = document.createElement('a'); if (window.URL && window.Blob && ('download' in a) && window.atob) < // Do it the HTML5 compliant way var blob = base64ToBlob(result.download.data, result.download.mimetype); var url = window.URL.createObjectURL(blob); a.href = url; a.download = result.download.filename; a.click(); window.URL.revokeObjectURL(url); >

The function base64ToBlob was taken from here and must be used in compliance with this function

function base64ToBlob(base64, mimetype, slicesize) < if (!window.atob || !window.Uint8Array) < // The current browser doesn't have the atob function. Cannot continue return null; >mimetype = mimetype || ''; slicesize = slicesize || 512; var bytechars = atob(base64); var bytearrays = []; for (var offset = 0; offset < bytechars.length; offset += slicesize) < var slice = bytechars.slice(offset, offset + slicesize); var bytenums = new Array(slice.length); for (var i = 0; i < slice.length; i++) < bytenums[i] = slice.charCodeAt(i); >var bytearray = new Uint8Array(bytenums); bytearrays[bytearrays.length] = bytearray; > return new Blob(bytearrays, ); >; 

This is good if your server is dumping filedata to be saved. However, I've not quite worked out how one would implement a HTML4 fallback

1 1 1 silver badge answered Apr 29, 2015 at 9:03 Luke Madhanga Luke Madhanga 7,377 2 2 gold badges 47 47 silver badges 48 48 bronze badges The a.click() does not seem to work in firefox. Any idea? Commented Nov 14, 2017 at 18:34

In some browsers you might need to add the a to the dom in order for this code to work and/or remove the revokeObjectURL part: document.body.appendChild(a)

Commented Nov 14, 2017 at 21:26

saved my day (and possibly a job too :) ) Not a javascript expert by any measure. more java guy. However, I have no idea why a simple "createObjectURL(new Blob([atob(base64)]))" doesn't work! It simply doesn't, while all instinct says it must. grrr.

Commented Mar 21, 2019 at 19:55

at line var bytechars = atob(base64) it throws an error JavaScript runtime error: InvalidCharacterError . I am using Chrome Version 75.0.3770.142 but i dont know, what is wrong here.

Commented Aug 7, 2019 at 9:23

The simple way to make the browser downloads a file is to make the request like that:

 function downloadFile(urlToSend) < var req = new XMLHttpRequest(); req.open("GET", urlToSend, true); req.responseType = "blob"; req.onload = function (event) < var blob = req.response; var fileName = req.getResponseHeader("fileName") //if you have the fileName header available var link=document.createElement('a'); link.href=window.URL.createObjectURL(blob); link.download=fileName; link.click(); >; req.send(); > 

This opens the browser download pop up.

answered Mar 16, 2017 at 9:42 João Marcos João Marcos 3,942 1 1 gold badge 20 20 silver badges 14 14 bronze badges

Thank you, I used this solution. Worked like a charm. Also, if you don't get a blob from the response, just create a new Blob.

Commented Oct 3, 2017 at 7:22 A better version with IE handling link Commented Jun 7, 2018 at 8:03 The link from @startsWith_R really helps if you are working with IE11 Commented Oct 23, 2019 at 3:24

1. Framework agnostic: Servlet downloading file as attachment

  download download 

2. Struts2 Framework: Action downloading file as attachment

  download download 

It would be better to use tag pointing with OGNL to an URL created with tag:

  value1  download 

In the above cases, you need to write the Content-Disposition header to the response, specifying that the file needs to be downloaded ( attachment ) and not opened by the browser ( inline ). You need to specify the Content Type too, and you may want to add the file name and length (to help the browser drawing a realistic progressbar).

For example, when downloading a ZIP:

response.setContentType("application/zip"); response.addHeader("Content-Disposition", "attachment; filename=\"name of my file.zip\""); response.setHeader("Content-Length", myFile.length()); // or myByte[].length. 

With Struts2 (unless you are using the Action as a Servlet, an hack for direct streaming, for example), you don't need to directly write anything to the response; simply using the Stream result type and configuring it in struts.xml will work: EXAMPLE

 application/zip attachment;filename="$" $ 

3. Framework agnostic (/ Struts2 framework): Servlet(/Action) opening file inside the browser

If you want to open the file inside the browser, instead of downloading it, the Content-disposition must be set to inline, but the target can't be the current window location; you must target a new window created by javascript, an in the page, or a new window created on-the-fly with the "discussed" target="_blank":

  download  download  download 
1 1 1 silver badge answered Jun 3, 2013 at 15:53 Andrea Ligios Andrea Ligios 50k 28 28 gold badges 119 119 silver badges 246 246 bronze badges Sir, Your input: "Content-Disposition", "inline;. saved the poor coder's day :) Commented May 29, 2015 at 14:23 This is the only answer that mentions "window.open" (one of the comments mentions it). Commented May 19, 2019 at 18:31 It does not work if you have a lot of parameters, because you will get too long url error. Commented Aug 7, 2019 at 9:25

I have created little function as workaround solution (inspired by @JohnCulviner plugin):

// creates iframe and form in it with hidden field, // then submit form with provided data // url - form url // data - data to form field // input_name - form hidden input name function ajax_download(url, data, input_name) " ).appendTo("body"); > iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument; if (iframe_doc.document) < iframe_doc = iframe_doc.document; >iframe_html = " 
" + "
" + ""; iframe_doc.open(); iframe_doc.write(iframe_html); $(iframe_doc).find('form').submit(); >

Demo with click event:

$('#someid').on('click', function() < ajax_download('/download.action', , 'dataname'); >); 
answered Jan 16, 2014 at 14:11 22.5k 6 6 gold badges 59 59 silver badges 72 72 bronze badges

That sends the data in a very strange way to the server though. I wonder if it could be altered to create compliant POST?

Commented Jan 19, 2014 at 21:44

I faced the same issue and successfully solved it. My use-case is this.

"Post JSON data to the server and receive an excel file. That excel file is created by the server and returned as a response to the client. Download that response as a file with custom name in browser"

$("#my-button").on("click", function()< // Data to post data = < ids: [1, 2, 3, 4, 5] >; // Use XMLHttpRequest instead of Jquery $ajax xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() < var a; if (xhttp.readyState === 4 && xhttp.status === 200) < // Trick for making downloadable link a = document.createElement('a'); a.href = window.URL.createObjectURL(xhttp.response); // Give filename you wish to download a.download = "test-file.xls"; a.style.display = 'none'; document.body.appendChild(a); a.click(); >>; // Post data to URL which handles post request xhttp.open("POST", excelDownloadUrl); xhttp.setRequestHeader("Content-Type", "application/json"); // You should set responseType as blob for binary responses xhttp.responseType = 'blob'; xhttp.send(JSON.stringify(data)); >); 

The above snippet is just doing following

Here we need to carefully set few things on the server side. I set few headers in Python Django HttpResponse. You need to set them accordingly if you use other programming languages.

# In python django code response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") 

Since I download xls(excel) here, I adjusted contentType to above one. You need to set it according to your file type. You can use this technique to download any kind of files.

answered Aug 9, 2016 at 12:44 Naren Yellavula Naren Yellavula 7,603 2 2 gold badges 30 30 silver badges 24 24 bronze badges

"We cannot download the file through Ajax, must use XMLHttpRequest". XMLHttpRequest is AJAX by definition. Otherwise great solution for modern web browsers. For IE, which doesn't support HTMLAnchorElement.download , I'm thinking of combining it with the proprietary msSaveOrOpenBlob method.

Commented Oct 4, 2018 at 6:49

Ok, based on ndpu's code heres an improved (I think) version of ajax_download;-

function ajax_download(url, data) " ).appendTo("body"); > iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument; if (iframe_doc.document) < iframe_doc = iframe_doc.document; >iframe_html = " 
" Object.keys(data).forEach(function(key)< iframe_html += ""; >); iframe_html +="
"; iframe_doc.open(); iframe_doc.write(iframe_html); $(iframe_doc).find('form').submit(); >

Use this like this;-

$('#someid').on('click', function() < ajax_download('/download.action', ); >); 

The params are sent as proper post params as if coming from an input rather than as a json encoded string as per the previous example.

CAVEAT: Be wary about the potential for variable injection on those forms. There might be a safer way to encode those variables. Alternatively contemplate escaping them.

answered Jan 19, 2014 at 21:52 1,623 1 1 gold badge 17 17 silver badges 21 21 bronze badges

This is working example. Thanks. Is it possible to do that without iframe but without window.location ?

Commented Oct 15, 2014 at 13:57

I suppose you could just append the hidden form to the bottom of the DOM. Also possibly worth exploring is use of the Shadow dom , although thats not necessarily well supported on older browsers.

Commented Oct 17, 2014 at 2:44

In this code I am getting this error. Uncaught SecurityError: Blocked a frame with origin "http://foo.bar.com" from accessing a frame with origin "null". The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "data". Protocols must match.

Commented Jan 16, 2016 at 10:09

How can I map this form to some model class? I have: @ResourceMapping() public void downloadFile(final ResourceRequest request, final ResourceResponse response, @ModelAttribute("downForm") FormModel model) but it's not working..

Commented Jan 12, 2017 at 9:23

void : That would likely be some sort of cross origin security issue. Thats probably a whole stack overflow question in and of it self. @bartex9 : That would depend heavily on what sort of framework your using. But the principle would be to take the name and path and store that, whilst pushing the file itself into a web accessible area of the file system, or something like amazon S3 for high availability

Commented Jan 13, 2017 at 2:23

My approach is completly based on jQuery. The problem for me was that it has to be a POST-HTTP call. And I wanted it to be done by jQuery alone.

$.ajax(< type: "POST", url: "/some/webpage", headers: , data: additionalDataToSend, dataType: "text", success: function(result) < let blob = new Blob([result], < type: "application/octetstream" >); let a = document.createElement('a'); a.href = window.URL.createObjectURL(blob); a.download = "test.xml";; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(a.href); . >, error: errorDialog >); 

What I and many others do is to create a link on the webpage, indicating that the target should be downloaded and putting the result of the http-request as the target. After that I append the link to the document than simply clicking the link and removing the link afterwards. You don't need an iframe anymore.

The magic lies in the lines

let blob = new Blob([result], < type: "application/octetstream" >); a.href = window.URL.createObjectURL(blob); 

The interesting point is that this solution is only working with a "blob". As you can see in other answers, some are simply using a blob but not explaining why and how to create it. As you can read e.g. in the Mozilla developer documentation you need a file, media ressource or blob for the function "createObjectURL()" to work. The problem is that your http-response might not be any of those. Therefore the first thing you must do is to convert your response to a blob. This is what the first line does. Then you can use the "createObjectURL" with your newly created blob. If you than click the link your browser will open a file-save dialog and you can save your data. Obviously it s possible that you cannot define a fixed filename for your file to download. Then you must make your response more complex like in the answer from Luke.

And don't forget to free up the memory especially when you are working with large files. For more examples and information you can look at the details of the JS blob object