Force Download via Ajax and PHP

45,180

Solution 1

You can't download files with ajax. So, if you have something that should happen on ajax, you should return url in response and apply it like document.location = "url"to start download process.

One note here. As I remember, browser will block file download if it is initiated not by user click. So, this will work fine:

.click(function(){
   document.location = "download url"
})

But if it is started not by user click, it will be blocked. So, code like this:

.click(function(){
       $.ajax({...,
       success:function(download_url_from_server){
           document.location = download_url_from_server;
       }});           
    })

will be blocked by browser. So, if you want to pass some data with a post, you may submit a form into hidden iframe or to blank page using <form target="...":

 function checkToken(token){
    var $form = $("#downloadForm");
    if ($form.length == 0) {
        $form = $("<form>").attr({ "target": "_blank", "id": "downloadForm", "method": "POST", "action": "script.php" }).hide();
        $("body").append($form);
    }
    $form.find("input").remove();
    var args = { a: "checkToken", b: token }
    for (var field in args) {
        $form.append($("<input>").attr({"value":args[field], "name":field}));
    }
    $form.submit();
}

And in script.php you need to execute code from download.php immediately, if token is Ok, or do a redirect to download script:

header("Location: download.php?a=" . $filename)

Solution 2

Setting the mime type to image/jpeg will most probably not work. So, you need application/octet-stream instead to force the download.

Replace the content type header in your php with the following:

header('Content-Type: application/octet-stream'); 

Also, One nice solution instead of using document.location is to inject an iframe. Use the following function in your success callback

function downloadFile(url)
    {
        var iframe;
        iframe = document.getElementById("download-container");
        if (iframe === null)
        {
            iframe = document.createElement('iframe');  
            iframe.id = "download-container";
            iframe.style.visibility = 'hidden';
            document.body.appendChild(iframe);
        }
        iframe.src = url;   
    }

Solution 3

It seems you have errors in your script. First of all, correct speliing for GET variable is $_GET['a'], not $GET['a']. The second issue here is that you have extra opening parenthesis, when I copied your code, I received 500 Internal Server Error response. If we correct mistakes, it seems to work fine. Just try corrected version of your code.

<?php
    header("Pragma: public"); // required
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Content-Description: File Transfer");
    header("Content-Type: image/jpg");
    header('Content-Disposition: attachment; filename="'.basename($_GET['a']).'"');
    header("Content-Transfer-Encoding: binary");
    header("Content-Length: ".filesize($_GET['a']));
    readfile($_GET['a']);
?>
Share:
45,180
Sylnois
Author by

Sylnois

Updated on July 20, 2022

Comments

  • Sylnois
    Sylnois almost 2 years

    i want to create a downloadscript which allows Force Download of JPGs. This is my php script:

    <?php
        header("Pragma: public"); // required
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Content-Description: File Transfer");
        header("Content-Type: image/jpg");
        header('Content-Disposition: attachment; filename="'.basename($GET['a']).'"');
        header("Content-Transfer-Encoding: binary");
        header("Content-Length: ".filesize(($GET['a']));
        readfile(($GET['a']);
    ?>
    

    This is a code segment of my js code:

    function downloadFile(a){
        document.location = "download.php?a="+ a;
    }
    

    With this code sample nothing happens. If i append the result into a HTML-tag, it shows the content of the file.

    Any ideas how to teach the browser to download this file?

    EDIT: SCRIPT UPDATE

  • Sylnois
    Sylnois about 11 years
    Yeah, but i want to force download JPGs and not to show it in the browser. Is there no way to do it with PHP-headers?
  • Viktor S.
    Viktor S. about 11 years
    Content disposition: attachment should do that.
  • Sylnois
    Sylnois about 11 years
    As you can see in the question bellow, that i added this already.
  • Viktor S.
    Viktor S. about 11 years
    Yeah. But are you still trying to do that with ajax?
  • Viktor S.
    Viktor S. about 11 years
    I just checked it on my project, and content-disposition: attachment; works fine. But you must do document.location = "url to php file"
  • jamesmillerio
    jamesmillerio about 11 years
    This seems overly complicated. If you simply redirect the user to a page that downloads the file, so long as you have the appropriate headers set, it will download the file in the exact same way without navigating them away from the page. The two examples achieve the same result, but one with less complexity.
  • dlock
    dlock about 11 years
    But, wouldn't it change the url in the address bar? :)
  • Sylnois
    Sylnois about 11 years
    give me a minute to test this
  • Sylnois
    Sylnois about 11 years
    Look at my question bellow. I changed my script. But still doesn't work. Thanks for your help.
  • Viktor S.
    Viktor S. about 11 years
    When you open link: "mysite.com/download.php?a=file.txt" directly in browser... What happens than? Also, how do you call downloadFile? Is it still in success function of ajax call?
  • Sylnois
    Sylnois about 11 years
    Server error 500. Yep, i call a ajax call to find the path with the filename. In the success function i transfer the result(filepath) to the download function which you can see above.
  • jamesmillerio
    jamesmillerio about 11 years
    No, it will simply prompt them to save the file without navigating away. The URL should remain unchanged.
  • Viktor S.
    Viktor S. about 11 years
    First: try to enter url with a file name directly into address bar, like: mysite.com/download.php?a=file.txt . Does it work?
  • Sylnois
    Sylnois about 11 years
    to header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Length: ' . filesize($fullPath)); header('Content-Disposition: attachment; filename="' . basename($_GET['a']) . '"'); readfile($_GET['a']);
  • Sylnois
    Sylnois about 11 years
    Another Update, i change the header conte-type to image/jpg. No IE8 blocks the content and asks me if i want to download it. I permit it but nothing happens
  • Viktor S.
    Viktor S. about 11 years
    Can you show an ajax call code? Not sure why nothing happens, but suppose that is because of block.
  • Sylnois
    Sylnois about 11 years
    function checkToken(token){ $.ajax({ url: "script.php", type: 'POST', data: ({ a: "checkToken", b: token }), success: function(msg){ downloadFile(msg); }, error: function(xhr, err){ alert("readyState: "+xhr.readyState+"\nstatus: "+xhr.status); alert("responseText: "+xhr.responseText); } }); }
  • Viktor S.
    Viktor S. about 11 years
    See updated answer about how you can avoid your request being blocked
  • Sylnois
    Sylnois about 11 years
    It works! I just added a visible input for the pathfile. If checkToken is successful, it writes the path in it. And when you submit the downloadbutton, he gets the content of the hidden input and calls the download function. Thank you very much for your help!
  • Aditya M P
    Aditya M P about 10 years
    I really like this solution, far more flexible when dealing with a special situation like no jQuery and doesn't depend on being inside a click callback exclusively. Thanks!
  • Zac Imboden
    Zac Imboden over 9 years
    This was the missing piece of the puzzle for me, what the server should send down.
  • Alexandre M
    Alexandre M over 3 years
    Actually it works in WebKit browsers but not in Firefox