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
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)
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;
}
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']);
?>
You're getting it confused a bit. As FAngel pointed out, you can't download files via AJAX. What you need to do is redirect the user to another page that then has your above PHP code in it. That PHP code should then allow the user to download the file directly. What you're attempting is absolutely possible, you just need to approach it from another direction, ie not with AJAX.
You can force download file with Mouse middle event:
const url = "https://www.google.com.vn/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png";
const forceDownload = url => {
try {
const link = document.createElement('a');
const fileName = url.substring(url.lastIndexOf('/') + 1, url.length);
const event = new MouseEvent( "click", { "button": 1, "which": 1 });
link.href = url;
link.download = fileName;
link.dispatchEvent(event);
} catch(e) {
document.location = url;
}
}
forceDownload(url);
Related
I have looked through a few similar questions but can't quite find what I am looking for (please don't mark this as duplicate as I did try to find an answer without posting a question)
When the user clicks on a button, an ajax request is sent to the controller where I am getting data back from the model. I am then converting it to a csv format and on success of the ajax call I want the file to download. I have everything working except the download part. I have seen some examples where you just redirect but that doesn't download anything, it shows a new page with the results.
$( '.spExcel' ).on('click', function() {
$.ajax({
url: url + '/Widgets/exportSpExcel',
type: 'POST',
})
.done(function (data) {
window.location.assign(data);
})
});
PHP:
if($_SERVER['REQUEST_METHOD'] === 'POST') {
$results = $this->DashboardModel->listPeople();
$filename = 'People_' . date('dmY') . '.csv';
header("Content-Description: File Transfer");
header("Content-Type: application/csv");
header("Content-Disposition: attachment; filename=$filename");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
$handle = fopen('php://output', 'w');
$header = array("Name", "Contact Number");
fputcsv($handle, $header);
foreach ($results as $result):
fputcsv($handle, $result);
endforeach;
fclose($handle);
}
Ajax isn't capable of writing a downloaded file - the browser has to do that itself. You could use window.open() but that would open the file in a new tab or window, which would then close immediately. That can look messy - it works but isn't ideal.
The simplest way to deal with this is to make the link download the response directly, without trying to use Ajax. Change the link to suit your needs, but it would be something like this...
<a href="/Widgets/exportSpExcel" class="spExcel" download>click to download</a>
Just add the download attribute to a link. It really is that simple :)
I'm using phpWord to create a word document on the fly in a php script that is called using XMLHttpRequest. I'm trapping the response to the request, and then attempting to prompt the user to download or open the file. My phpWord code creates the file OK (I can open the file on the server), and the browser prompts the user to open or save the file, but the file that is downloaded is corrupted somehow. It gives an error like "We're sorry. We can't open results.docx because we found a problem with its contents".
On the server side I have this code:
...
header('Content-Description: File Transfer');
header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document' );
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Transfer-Encoding: binary');
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($filename));
flush();
readfile($filename);
On the client side I have this code:
function getDOC()
{
url='services/doc_search_results_service.php/';
var req = null;
var postParms = '';
req = new XMLHttpRequest
if ( req )
{
req.open( 'POST', url, true );
req.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );
req.setRequestHeader( "Content-length", postParms.length );
req.setRequestHeader( "Connection", "close" );
req.onreadystatechange = function()
{
if ( req.readyState == 4 && req.status == 200 )
{
downloadDOC( "results.docx", req.responseText );
}
}
req.send( postParms );
}
}
function downloadDOC(filename, text)
{
var pom = document.createElement('a');
pom.setAttribute('href', 'data:application/vnd.openxmlformats-officedocument.wordprocessingml.document,' + encodeURIComponent(text));
pom.setAttribute('download', filename);
document.body.appendChild(pom);
pom.click();
document.body.removeChild(pom);
}
Would love to know if anyone can spot where I'm going wrong. I'm not an expert in php nor javascript, so any help would be gratefully received. FWIW I think it may be to do with headers. I'm also guessing that it may well be a client side problem as the file is create OK on the server side.
Thanks in advance. :-)
Thanks to #Musa for the suggestion to use 'GET' rather than 'POST' which worked perfectly.
As #Musa suggested, the client-side javascript in this example should be:
window.location = 'services/doc_search_results_service.php/';
Which replaces all the javascript quoted in my original question.
Note - I'm not sure what the correct answer would have been if it had not been possible to use GET for some reason.
I'm making an AJAX call to a site which generates a query and then saves it to a .txt file.
This file should be downloaded after the AJAX was done and close that download window.
Howether IE closes it automatically and then tries to close the mainwindow, which shouldn't be closed.
Meanwhile Chrome only closes the download window which is what IE should do aswell..
Is there a workaround for this?
function start(){
$.ajax({
url: ('query.php'),
type: 'POST',
async:false,
data: {
id: id,
intnr: intnr
},
dataType: "html"
})
.done (function(response) { window.open('download.php'); window.close('download.php'); })
.fail (function(xhr, textStatus, errorThrown) { alert(xhr.responseText); })
;
}
download.php is just :
<?php
header ( 'Content-Type: text/html');
header ( "Content-Disposition: 'attachment'; filename='File.txt'");
include ('/xx/xx/query.txt');
?>
EDIT : Workaround but it is working now..
shortened function to
.done (function(response) { var download_window = window.open('download.php'); })
added into download.php
<script>
var s = navigator.userAgent;
if(s.indexOf("Chrome") > -1 == true)
{
window.open('', '_self', '');
window.close();
}
</script>
How about this then:
.done (function(response) {
var download_window = window.open('download.php');
download_window.close();
})
.. should make IE not close anything else.
This doesn't really answer your question, but offers an alternative.
Try something like this in the jQuery code:
.done (function(response) { window.location.href = "download.php"; })
.. and add headers to force download in download.php:
header("Content-Description: File Transfer");
header("Content-Type: application/download");
header("Content-Type: application/force-download");
// Removed this from my code.
// header("Content-Type: application/octet-stream");
// Added this for yours.. not sure exactly what's optimal for your case.
header("Content-Type: text/html");
header("Content-Transfer-Encoding: binary");
header("Content-Disposition: attachment; filename=File.txt");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Expires: 0");
header("Pragma: public");
ob_clean();
flush();
readfile("/xx/xx/query.txt");
In my project, this php code was run after clicking a submit button (form) and if I recall correctly, it just showed a download dialogue box without updating the address bar or showing an empty page.
This will work if it is rad window
function GetRadWindow() {
var oWindow = null;
if (window.radWindow)
oWindow = window.radWindow;
else if (window.frameElement && window.frameElement.radWindow)
oWindow = window.frameElement.radWindow;
return oWindow;
}
function selfClose(){
GetRadWindow().close();
}
I'm creating image using canvas and using following script,
function getImage() {
var canvas1 = document.getElementById("images");
if (canvas1.getContext) {
var ctx = canvas1.getContext("2d");
var myImage = canvas1.toDataURL("image/jpg");
}
$('<form action="download.php" method="POST">' +
'<input type="hidden" name="aid" value="' + myImage + '">' +
'</form>').submit();
}
And in my Download.php file is,
<?php $img = $_POST['aid'];
echo "<img src=".$img.">";
?>
it showing image correctly. But i wanna give download button with jpg format or pdf format.
How i can use?
I used base64_decode(); method. But i cant solve.
Help me...
Thanks for all. but I got answer using,
file_put_contents();
But thing, i dont know how to use. Finally i got it from this Answer.
Answer is,
$data = 'data:image/png;base64,AAAFBfj42Pj4';
list($type, $data) = explode(';', $data);
list(, $data) = explode(',', $data);
$data = base64_decode($data);
file_put_contents('/tmp/image.png', $data);
But still i'm waiting for download button with option of image/pdf format.
Try this:
PHP echo'ed image with link
<?php
$img = $_POST['aid'];
echo "";
?>
download_image.php
<?php
$img = "myimage.jpg";
// fix for IE catching or PHP bug issue
header("Pragma: public");
header("Expires: 0"); // set expiration time
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
// browser must download file from server instead of cache
// force download dialog
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
// use the Content-Disposition header to supply a recommended filename and
// force the browser to display the save dialog.
header("Content-Disposition: attachment; filename=".basename($img).";");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($img));
readfile("$img");
exit();
?>
This question already has answers here:
PHP generate file for download then redirect
(11 answers)
Closed 3 years ago.
Page 1 links to page 2. Page 2 serves a download using the following code:
header("Content-disposition: attachment; filename= '$filename'");
header('Content-type: application/pdf');
readfile($file);
header("location: mainpage.php");
The result being the user "stays" on page 1 but is served a download.
How can I set things up, so that users remain on page 1 but it refreshes after the download is served.
I don't know javascript so I am hoping for a purely PHP solution.
In my opinion I wouldn't think you would necessarily need to refresh page1 at all. You should be able to force the download via a link within page1. See below:
Page1.php with a link
Download PDF
Page2.php
$filename = $_GET['pdf'] . '.pdf';
header('Content-type: application/pdf');
header("Content-disposition: attachment; filename= '$filename'");
header("location: $filename");
This will allow the download to start whilst you remain on page1.
Hope this is what you had in mind.
Didn't know of this before, but it's just one of the nice HTTP headers and most of us already know of it from HTML: Refresh.
Just add the following header call:
header('Refresh: 0; url=http://stackoverflow.com/');
You can check what the referer is by $_SERVER['HTTP_REFERER']. So you should be able to put this in you're page1.php:
if($_SERVER['HTTP_REFERER'] == page2.php) {
echo "<meta http-equiv=\"refresh\" content=\"0;url=http://www.yourdomain.com/page1.php\">";
exit();
}
This way, you check if your visitor is coming from page2.php, and if they are, you only parse a meta-tag which refresh the browser. When it is refreshed, it wouldn't refresh again, because the HTTP_REFERER is now page1.php.
No sure if you ever got this answered, but I had the same problem,
here is my solution
The AJAX jquery
$(function(){
$("#itemList").on("click", "a.downloadLink", function(){ //this binds a click event handler on the itemList container that will listen out for any a with class of downloadLink inside it being clicked
var link = $(this);
var item = link.parent();
var forId = item.data("itemid");
var started = new Date(); //the alternative to tracking time elapsed is to just use a simple counter you increment - "poll 5 times" etc. if your doing .5 second intervals, then 5 times = 2.5 seconds for example. Time elapsed may result in less polls, if a poll takes a long time to return, for example. Use whichever approach feels better.
var maxTime = 5000; //5 seconds
function poll(){
$.ajax({
type: "POST",
url: "Watergetstatus.php",
data: {FID: forId}, //this will be turned into a request for page1?forId=1&oldValue=2 - I expect it in this example to return a json-encoded response of {"changed":true|false, "newHtml":"replacementContent on success"}
datatype :'json',
success: function(data){
if (data.changed=true)
{
window.location.reload(true);
}
else {
var elapsed = (new Date())-started;
//window.location.reload(true);
if (elapsed <= maxTime) setTimeout(poll, 500); // Poll again in .5 seconds
//else you can assume the link didn't open / work / the database never changed etc - handle or ignore as needed
}
},
error: function() {
alert("borken"); //the request to the server bombed out; up to you if you want to simply re-queue for another try like above until the expiry time or if you want to show an error or just simply ignore it.
}
});
}
setTimeout(poll, 500); //wait 0.5 seconds before polling
});
});
The HTML had a large list of dynamic links
<div data-itemid="<?php echo $row_Files['FID']; ?> " >
<a class="downloadLink" href="PLC_FILES/WaterCheckOut.php?FID=<?php echo $row_Files['FID']; ?> " target=""><img src="PLCImages/download.fw.png"></a>
</div>
The Poling file watergetstatus.php
mysql_select_db($database_PLC, $PLC);
$query_files = "SELECT * FROM files WHERE FID = '{$_POST['FID']}'";
$files = mysql_query($query_files, $PLC) or die(mysql_error());
$row_files = mysql_fetch_assoc($files);
$totalRows_files = mysql_num_rows($files);
if($row_files['Status'] ==2)
{
$data= array("changed"=>true);
echo json_encode($data);
}
else
{
$data= array("changed"=>false);
echo json_encode($data);
}
THe download file link watercheckout.php
if ( $row_files['Status']==1 ) {
$file_path = $row_files['FileName'];;
$path_parts = pathinfo($file_path);
$file_name = $path_parts['basename'];
$file_ext = $path_parts['extension'];
$content_types = array(
"exe" => "application/octet-stream",
"zip" => "application/zip",
"mp3" => "audio/mpeg",
"mpg" => "video/mpeg",
"avi" => "video/x-msvideo",
);
$ctype = isset($content_types[$file_ext]) ? $content_types[$file_ext] : $ctype_default;
$file = $row_files['FileName'];
$path = "Historical/".date('Y-m-d-His');
$newfile = $path."_".$file;
$today = date('Y-m-d H:i:s');
header('Content-disposition: attachment; filename='.$file);
header("Content-Type: " . $ctype);
header('Content-Length: ' . filesize($file));
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate');
header('Pragma: public');
mysql_select_db($database_PLC, $PLC);
mysql_query("UPDATE files SET Status = '2'");
ob_clean();
flush();
readfile($file);
rename($file, $newfile);
I have simplified my code and cut out alot, so it may be missing something, but this was the general frame work that I used and it works for me
Hope this helps someone, as there was not much out there
//----------------- TOP OF DOWNLOAD_PAGE.PHP ----------------------
$download_code = mysql_real_escape_string(urldecode($_GET['code']));
$download = mysql_real_escape_string(urldecode($_GET['download']));
$self = $_SERVER["PHP_SELF"]."?code=$download_code";
//refresh page after download...
echo"
<script type=\"text/javascript\">
function downloadRedirect(){
var redirect_url = \"$self\";
setTimeout(\"DoTheRedirect('\"+redirect_url+\"')\",
parseInt(0.5*1000));
}
function DoTheRedirect(url) { window.location=url; }
</script>
";
...
$filepath = "/var/www/vhosts/YOUR_DOMAIN.com/digital_downloads/";
if (isset($_GET['download'])) {
$file = $_GET['download'];
if (file_exists($filepath.$download) &&
is_readable($filepath.$download) && (preg_match('/\.zip$/',$download) || preg_match('/\.zipx$/',$download) )) {
...
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$download."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filepath.$download));
ob_end_flush();
readfile($filepath.$download);
}//end if file_exists
}//end if isset
//----------------- BOTTOM HALF OF DOWNLOAD_PAGE.PHP ----------------------
//link generated to download and call JS to refresh page
echo "Click to Download