Force Download via Ajax and PHP
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']);
?>
Sylnois
Updated on July 20, 2022Comments
-
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 about 11 yearsYeah, 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. about 11 yearsContent disposition: attachment should do that.
-
Sylnois about 11 yearsAs you can see in the question bellow, that i added this already.
-
Viktor S. about 11 yearsYeah. But are you still trying to do that with ajax?
-
Viktor S. about 11 yearsI just checked it on my project, and content-disposition: attachment; works fine. But you must do document.location = "url to php file"
-
jamesmillerio about 11 yearsThis 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 about 11 yearsBut, wouldn't it change the url in the address bar? :)
-
Sylnois about 11 yearsgive me a minute to test this
-
Sylnois about 11 yearsLook at my question bellow. I changed my script. But still doesn't work. Thanks for your help.
-
Viktor S. about 11 yearsWhen 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 about 11 yearsServer 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 about 11 yearsNo, it will simply prompt them to save the file without navigating away. The URL should remain unchanged.
-
Viktor S. about 11 yearsFirst: try to enter url with a file name directly into address bar, like: mysite.com/download.php?a=file.txt . Does it work?
-
Sylnois about 11 yearsto 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 about 11 yearsAnother 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. about 11 yearsCan you show an ajax call code? Not sure why nothing happens, but suppose that is because of block.
-
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. about 11 yearsSee updated answer about how you can avoid your request being blocked
-
Sylnois about 11 yearsIt 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 about 10 yearsI 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 over 9 yearsThis was the missing piece of the puzzle for me, what the server should send down.
-
Alexandre M over 3 yearsActually it works in WebKit browsers but not in Firefox