download file using an ajax request

371,752

Solution 1

Update April 27, 2015

Up and coming to the HTML5 scene is the download attribute. It's supported in Firefox and Chrome, and soon to come to IE11. Depending on your needs, you could use it instead of an AJAX request (or using window.location) so long as the file you want to download is on the same origin as your site.

You could always make the AJAX request/window.location a fallback by using some JavaScript to test if download is supported and if not, switching it to call window.location.

Original answer

You can't have an AJAX request open the download prompt since you physically have to navigate to the file to prompt for download. Instead, you could use a success function to navigate to download.php. This will open the download prompt but won't change the current page.

$.ajax({
    url: 'download.php',
    type: 'POST',
    success: function() {
        window.location = 'download.php';
    }
});

Even though this answers the question, it's better to just use window.location and avoid the AJAX request entirely.

Solution 2

To make the browser downloads a file you need 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();
 }

Solution 3

You actually don't need ajax at all for this. If you just set "download.php" as the href on the button, or, if it's not a link use:

window.location = 'download.php';

The browser should recognise the binary download and not load the actual page but just serve the file as a download.

Solution 4

Cross browser solution, tested on Chrome, Firefox, Edge, IE11.

In the DOM, add an hidden link tag:

<a id="target" style="display: none"></a>

Then:

var req = new XMLHttpRequest();
req.open("GET", downloadUrl, true);
req.responseType = "blob";
req.setRequestHeader('my-custom-header', 'custom-value'); // adding some headers (if needed)

req.onload = function (event) {
  var blob = req.response;
  var fileName = null;
  var contentType = req.getResponseHeader("content-type");

  // IE/EDGE seems not returning some response header
  if (req.getResponseHeader("content-disposition")) {
    var contentDisposition = req.getResponseHeader("content-disposition");
    fileName = contentDisposition.substring(contentDisposition.indexOf("=")+1);
  } else {
    fileName = "unnamed." + contentType.substring(contentType.indexOf("/")+1);
  }

  if (window.navigator.msSaveOrOpenBlob) {
    // Internet Explorer
    window.navigator.msSaveOrOpenBlob(new Blob([blob], {type: contentType}), fileName);
  } else {
    var el = document.getElementById("target");
    el.href = window.URL.createObjectURL(blob);
    el.download = fileName;
    el.click();
  }
};
req.send();

Solution 5

It is possible. You can have the download started from inside an ajax function, for example, just after the .csv file is created.

I have an ajax function that exports a database of contacts to a .csv file, and just after it finishes, it automatically starts the .csv file download. So, after I get the responseText and everything is Ok, I redirect browser like this:

window.location="download.php?filename=export.csv";

My download.php file looks like this:

<?php

    $file = $_GET['filename'];

    header("Cache-Control: public");
    header("Content-Description: File Transfer");
    header("Content-Disposition: attachment; filename=".$file."");
    header("Content-Transfer-Encoding: binary");
    header("Content-Type: binary/octet-stream");
    readfile($file);

?>

There is no page refresh whatsoever and the file automatically starts downloading.

NOTE - Tested in the following browsers:

Chrome v37.0.2062.120 
Firefox v32.0.1
Opera v12.17
Internet Explorer v11
Share:
371,752

Related videos on Youtube

Manuel Di Iorio
Author by

Manuel Di Iorio

Updated on April 17, 2021

Comments

  • Manuel Di Iorio
    Manuel Di Iorio about 3 years

    I want to send an "ajax download request" when I click on a button, so I tried in this way:

    javascript:

    var xhr = new XMLHttpRequest();
    xhr.open("GET", "download.php");
    xhr.send();
    

    download.php:

    <?
    header("Cache-Control: public");
    header("Content-Description: File Transfer");
    header("Content-Disposition: attachment; filename= file.txt");
    header("Content-Transfer-Encoding: binary");    
    readfile("file.txt");
    ?>
    

    but doesn't work as expected, how can I do ? Thank you in advance

  • mikemaccana
    mikemaccana about 10 years
    The programming language you're using to change window.location is JavaScript.
  • Jelle Kralt
    Jelle Kralt about 10 years
    You're right @mikemaccana, I actually meant ajax :).
  • Gustavo Straube
    Gustavo Straube over 9 years
    As @ManuelDiIorio said, a simple window.location resolves the question. So I think the reply from Jelle Kralt below answers better the question.
  • user1447679
    user1447679 about 9 years
    Doesn't this call the link twice? I'm in a similar boat... I'm passing a lot of security information in headers, and able to parse the file object in the success function, but don't know how to trigger a download prompt.
  • mmmmmm
    mmmmmm about 8 years
    It does call the page twice, so if you are querying a database in that page, this means 2 trips to DB.
  • Yangshun Tay
    Yangshun Tay almost 8 years
    Have been hunting high and low for a solution and this is so elegant and perfect. Thank you so much.
  • Mickael Bergeron Néron
    Mickael Bergeron Néron almost 8 years
    Isn't this dangerous security-wise?
  • Pedro Sousa
    Pedro Sousa almost 8 years
    @MickaelBergeronNéron Why?
  • Mickael Bergeron Néron
    Mickael Bergeron Néron almost 8 years
    I would think so because anybody can call download.php?filename=[something] and try some path and file names, especially common ones, and this could even be done inside a loop within a program or a script.
  • Pedro Sousa
    Pedro Sousa almost 8 years
    wouldn't .htaccess avoid that?
  • Prof
    Prof almost 8 years
    @PedroSousa .. no. htaccess controls access to the file structure via Apache. Since the access has reached a PHP script, htaccess now stops its duty. This IS VERY MUCH a security hole because indeed, any file that PHP (and the user it is being run under) can read, so it can deliver into the readfile... One should always sanitise the requested file to be read
  • Pedro Sousa
    Pedro Sousa almost 8 years
    @prof83 I guess you are right. For the sake of security, we should always sanitise files to be requested. Thanks for poiting it out, i will definitely have that into account in the future. Thanks :)
  • John
    John almost 8 years
    @user1447679 see for an alternative solution: stackoverflow.com/questions/38665947/…
  • mickmackusa
    mickmackusa about 7 years
    Please format your entire code block and provide some additional explanation to your process for future reader benefit.
  • Erik Donohoo
    Erik Donohoo almost 7 years
    This works for me, but in firefox, I needed to first put an <a> tag in the DOM, and reference it as my link rather than create one on the fly in order for the file to download automatically.
  • Diego
    Diego almost 7 years
    works but what happens if the file is creating during execution? it doesnt work like when the file is created already.
  • Scott
    Scott almost 7 years
    Let me explain how this helped me... the example could have been more complete. with "download.php?get_file=true" or something... I have an ajax function that does some error checking on a form submission and then creates a csv file. If the error check fails, it has to come back with why it failed. If it creates the CSV it is telling the parent that "go ahead and fetch the file". I do that by posting to the ajax file with the form variable then posting a DIFFERENT parameter to the same file saying "hand me the file you just created" (path/name is hard coded into the ajax file).
  • krillgar
    krillgar over 6 years
    Of course, this solution will only work if it is a static file that already exists.
  • Dharmendrasinh Chudasama
    Dharmendrasinh Chudasama about 6 years
    But it will send request 2 times, that is not proper
  • Ian
    Ian almost 6 years
    If the server responds with an error though there won't be any way to stay on your main page without being redirected to an error page by the browser. At least this is what Chrome does when the result of window.location returns 404.
  • JSG
    JSG almost 6 years
    Downloading period is dangerous. Using a token system or some sort of download management is required for all answers here to keep them safe. These worries are valid for every one of them that doesn't have the security/management.
  • Steve Owens
    Steve Owens over 5 years
    So I see some comments where there is complaint that this is not useful but that is not always the case. Consider an S3 presigned url with customer provided keys on encryption. You still have to provide custom headers on the get to download the object so simply setting window.locaion = signedurl will not work you need to send the encryption key and other info in custom AWS specific headers so this is worthy of an upvote.
  • BPeela
    BPeela about 5 years
    You can just make the a tag hidden and populate the href dynamically. no need to add and remove
  • Sнаđошƒаӽ
    Sнаđошƒаӽ about 5 years
    @Taha I tested this on Edge and it seemed to work. Don't know about IE though. My client doesn't target IE users ;-) Am I lucky? :D
  • Márton Tamás
    Márton Tamás almost 5 years
    @ErikDonohoo You can create the <a> tag with JS, "on the fly" as well, but gotta append it to document.body. You certainly want to hide it at the same time.
  • Gonzalo De-Spirito
    Gonzalo De-Spirito almost 5 years
    This is a bad idea. it call the url twice. It makes no sense at all. Why dont you execulte window.location = 'download.php' without the ajax overhead?
  • vibs2006
    vibs2006 over 4 years
    Good Generic Code. @leo can you improve this code by adding custom headers like Authorization ?
  • vibs2006
    vibs2006 over 4 years
    Thanks @leo. Its helpful.Also what do you suggest addingwindow.URL.revokeObjectURL(el.href); after el.click() ?
  • Abhishek Gharai
    Abhishek Gharai over 4 years
    Thanks @João, I was looking for this solution from a very long time.
  • Mathew Alden
    Mathew Alden almost 4 years
    The filename will be wrong if the content disposition specifies a non-UTF8 filename.
  • CJ Broersma
    CJ Broersma almost 3 years
    wow I overthought it. I just added my file name as a GET param and bingo. Thanks!
  • Hughsie28
    Hughsie28 over 2 years
    Several years later and its still not supported in IE11, sad times.