I have a pdf file which is located off my webpage's root. I want to serve a file in ../cvs to my users using php.
Here is the code I have sofar:
header('Content-type: application/pdf');
$file = file_get_contents('/home/eamorr/sites/eios.com/www/cvs/'.$cv);
echo $file;
But when I call this php page, nothing gets printed! I'd like to simply serve the pdf file stored whose name is in $cv (e.g. $cv = 'xyz.pdf').
The ajax response to this PHP page returns the text of the pdf (gobbldy-gook!), but I want the file, not the gobbldy-gook!
I hope this makes sense.
Many thanks in advance,
Here's the AJAX I'm using
$('#getCurrentCV').click(function(){
var params={
type: "POST",
url: "./ajax/getCV.php",
data: "",
success: function(msg){
//msg is gobbldy-gook!
},
error: function(){
}
};
var result=$.ajax(params).responseText;
});
I'd like the user to be prompted to download the file.
Don't use XHR (Ajax), just link to a script like the one below. The HTTP headers the script outputs will instruct the browser to download the file, so the user will not navigate away from the current page.
<?php
// "sendfile.php"
//remove after testing - in particular, I'm concerned that our file is too large, and there's a memory_limit error happening that you're not seeing messages about.
error_reporting(E_ALL);
ini_set('display_errors',1);
$file = '/home/eamorr/sites/eios.com/www/cvs/'.$cv;
//check sanity and give meaning error messages
// (also, handle errors more gracefully here, you don't want to emit details about your
// filesystem in production code)
if (! file_exists($file)) die("$file does not exist!");
if (! is_readable($file)) die("$file is unreadable!");
//dump the file
header('Cache-Control: public');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="some-file.pdf"');
header('Content-Length: '.filesize($file));
readfile($file);
?>
Then, simplify your javascript:
$('#getCurrentCV').click(function(){
document.location.href="sendfile.php";
});
How about using readfile instead? Provided that the file exists, that should work. Make sure your web process has permission to read the directory and the file. There is an example on the readfile page that sets some headers as well.
I'm trying to prompt the user to download the pdf file.
You can't (and don't need to) send a binary download to the user's browser using Ajax. You need to send the user to an actual URL where the PDF is located.
Use #timdev's code, and point the user there using e.g.
location.href = "scriptname.php";
It sounds like you're trying to serve the pdf to user for download via AJAX.
What you want to do is use AJAX to confirm the files exists, and security if any, then simply use js to redirect the browser to that files url, or in this case the url of the php script delivering the pdf. When your browser gets the pdf header it wont try to redirect the page itself but prompt for download, or whatever the users browser settings are.
Something like:
(js)
window.location.href = http://example.com/getApdf.php?which=xyz
(php)
if( !isset( $_GET['which'] ) ) die( 'no file specified' );
if( !file_exists( $_GET['which'] . '.pdf' ) ) die( 'file doesnt exist');
header('Content-type: application/pdf');
readfile( $_GET['which'] . '.pdf' );
Related
I have a PHP app that creates a CSV file which is forced to download using headers. Here's the relevant part of the code:
header('Content-Type: application/csv');
header("Content-length: " . filesize($NewFile));
header('Content-Disposition: attachment; filename="' . $FileName . '"');
echo $content;
exit();
What I'd like to do is redirect users to a new page after the file is built and the download prompt is sent. Just adding header("Location: /newpage") to the end didn't work, expectedly, so I'm not sure how to rig this up.
I don't think this can be done - although I am not 100% sure.
The common thing (e.g. in popular download sites) is the reverse: first you go to the after page and then the download starts.
So redirect your users to the final page that (among other things) says:
Your download should start automatically. If not click [a href="create_csv.php"]here[/a].
As about initiating the download (e.g. automatically calling create_csv.php) you have many options:
HTML: [meta http-equiv="refresh" content="5;url=http://site/create_csv.php"]
Javascript: location.href = 'http://site/create_csv.php';
iframe: [iframe src="create_csv.php"][/iframe]
very easy to do in the case it is really needed.
But you will need to have a bit work in JavaScript and cookies:
in PHP you should add setting up a cookie
header('Set-Cookie: fileLoading=true');
then on the page where you call the download you should track with JS (e.g. once per second) if there is coming cookie like that (there is used plugin jQuery cookie here):
setInterval(function(){
if ($.cookie("fileLoading")) {
// clean the cookie for future downoads
$.removeCookie("fileLoading");
//redirect
location.href = "/newpage";
}
},1000);
Now if the file starts to be downoaded JS recognizes it and redirects to the page needed after cookie is deleted.
Of course, you can tell you need browser to accept cookies, JavaScript and so on, but it works.
The header you are sending are HTTP headers. The browser takes that as a page request and processes it as a page. And in your case, a page it needs to download.
So adding a redirect header to that confuses the whole process of downloading the file (since headers are collected, generated into one header and then sent to the browser, you can try this by setting multiple redirect headers IIRC)
This is quite old issue, but here is how I achieved it via JS.
// Capture the "click" event of the link.
var link = document.getElementById("the-link");
link.addEventListener("click", function(evt) {
// Stop the link from doing what it would normally do.
evt.preventDefault();
// Open the file download in a new window. (It should just
// show a normal file dialog)
window.open(this.href, "_blank");
// Then redirect the page you are on to whatever page you
// want shown once the download has been triggered.
window.location = "/thank_you.html";
}, true);
Via - https://www.daniweb.com/web-development/php/threads/463652/page-not-redirecting-after-sending-headers-in-php
Bear in mind, however, the automatic initiation of downloadable files for IE users will trigger the security warning tab. All three of the methods outlined by daremon would show this warning. You simply can't get around this. You will be better served if you provide real links.
<?php
function force_file_download($filepath, $filename = ''){
if($filename == ''){
$filename = basename($filepath);
}
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . $filename . "\"");
readfile($filepath); // do the double-download-dance (dirty but worky)
}
force_file_download('download.txt');
?>
<script type="text/javascript">
location = 'page2.php'
</script>
Here is a solution using javascript.
I found one workaround for this that relies on javascript, so it's not exactly secure, but for non-secure critical sites it seems to work.
Have a form with a button titled 'download' with the action set to point to the download script, then using javascript put something on the onsubmit handler that strips out the download button and replaces the messaging on the screen. The download should still happen and the screen will change. Obviously, if there's an issue with the download script then it still looks like the download was successful even if it doesn't fire, but it's the best I've got right now.
Update: New Solution:
<a id="download-link"
href="https://example.com/uploads/myfile.pdf"
class="btn">Download</a>
<script>
jQuery(document).ready(function( ) {
jQuery("#download-link").click(function(e) {
e.preventDefault();
var url = jQuery(this).attr('href');
var _filename = url.split('/');
_filename = _filename[_filename.length - 1];
console.log(_filename);
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
})
.then(response => response.blob())
.then(response => {
const blob = new Blob([response], {type: 'application/pdf'});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = _filename;
document.body.appendChild(a);
a.click();
});
});
});
</script>
Source
Old answer:
Here is the answer:
It's work!
You need three different parts of code:
HTML
<a id="download_btn" class="btn btn-primary" href="?file=filename&download=1">Download<&/a>
**JQuery**
$('#download_btn').click(function(){
window.location.href = '<?=base_url()?>/?file=<?=$file;?>&download=1';
}).focusout (function(){
window.location.href = '<?=base_url()?>';
return false;
});
**PHP**
if(isset($_GET['download']) && isset($_GET['file'])){
$zip_path = 'path_to/'.$_GET['file'].'.zip';
if(file_exists($zip_path)){
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="'.basename($zip_path).'"');
header('Content-Length: ' . filesize($zip_path));
header('Location: '.$zip_path);
}
}
You can try and redirect to the URL plus a parameter that represents the file contents. And in the redirect, you can output the file content for download.
Launch the PHP file which contains the CSV download using:
<a onclick="popoutWin(\'csvexport.php\')" >Download CSV File</a>
or
<input name="newThread" type="button" value="Download CSV File"
onclick="popoutWin(\'csvexport.php\')" />
where the JavaScript function popoutWin is
/*
* Popout window that self closes for use with downloads
*/
function popoutWin(link){
var popwin = window.open(link);
window.setTimeout(function(){
popwin.close();
}, 500);
}
This will open a window, to display the CSV download prompt and then immediately close the window, leaving only the prompt.
Hmmmm...
Put the code below in the main javascript of the site.
if (window.location.pathname == '/url_of_redirect/') {
window.open(location.protocol + '//' + location.hostname + '/url_of_download.zip', '_parent');
}
Explaining:
It does the normal redirect with php to some url you will difine and in javascipt it says: If the pathname is the same as the path that redirect made '/url_of_redirect/' then it opens the url on a new page, generating the downlod.
Is it possible to count only fully file downloads (when user click accept when save dialog apparents) using nginx or apache (php on backend, but nginx deliver files)?
A pure PHP solution could look like this (simplified):
<?php
$file = $_GET['file'];
check_if_file_is_ok_for_download($file);
header('Content-Type: ...');
header('Content-Length: ...');
header('Content-Dispostion: ...');
// more headers if necesarry ...
// output the file
readfile($file);
// count the finished download
database_add_finished_download($file);
Then use download links like:
http://yourserver.com/download.php?file=...
Which can be url-rewritten to something like:
http://yourserver.com/download/...
I have a php script that gets called via an ajax call. Values are sent to this script to build a pdf. I want to send the pdf to the browser, but since the script that builds the pdf returns to the page with the javascript I can't see how to do this. Any ideas?
I would recommend something a bit different. Instead of AJAX call make a redirect to an URL like this:
./path_to_pdf_script/script.php?param1=val1¶m2=val2
This script would be the one which generated the pdf. Place somewhere on top of the script this header:
header('Content-type: application/pdf');
And simply echo the string the pdf content is in. If you want the user to download this pdf instead of viewing you could do the AJAX call with the example found HERE:
from php.net
If you want the user to be prompted to save the data you are sending,
such as a generated PDF file, you can use the ยป Content-Disposition
header to supply a recommended filename and force the browser to
display the save dialog.
<?php
// We'll be outputting a PDF
header('Content-type: application/pdf');
// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');
// The PDF source is in original.pdf
readfile('original.pdf');
?>
You could use an iframe instead of an ajax request and force-download the pdf file.
As you noticed, your AJAX call can't directly output the PDF to the browser. One workaround is to remove AJAX and send the user directly to the page that generates the PDF. This approach is very common and well documented. But there is a way to use AJAX to generate the PDF, so that the user will stay on the web page until the file is ready.
Your AJAX call could answer with a JSON object with 2 exclusive fields:
"pdfurl" if the pdf file was successfully created and written to the disk,
"errormsg" if there was an error.
Something like (in PHP):
<?php
//...
if (writepdf($filename, ...)) {
$result = array('pdfurl' => '/files/' . $filename);
} else {
$result = array('errormsg' => 'Error!');
}
echo json_encode($result);
Then the page's javascript could contain (jQuery example):
$.ajax({
type: "GET",
url: "ajaxcreatepdf.php",
data: {userid: 1},
dataType: "json",
success: function(data, textStatus) {
if (data.pdfurl) {
window.location.href = data.pdfurl;
}
else {
$("#messagebox").html(data.errormsg);
}
}
});
The Ajax request is not direct visible to the user, so a redirect make no sense
You need to load this PDF into an existing or new browser window after the ajax has returned.
I'm trying to accomplish a fairly simple task for my website, but I"m not sure exactly how to go about it. I want the user to be viewing a table, then click a button, at which point the user can save the contents of that table as a csv file. This request can sometimes be quite complicated so I generate a progress page to alert the user.
I have most things figured out except actually generating the csv file. (I use jQuery and PHP)
the jQuery code run on click:
hmis_query_csv_export: function(query_name) {
$.uiLock('<p>Query Loading.</p><img src="/images/loading.gif" />')
$.get({
url: '/php_scripts/utils/csv_export.php',
data: {query_name: query_name},
success: function(data) {
$.uiUnlock();
}
});}
the relevant PHP:
header("Content-type: text/x-csv");
header("Content-Disposition: attachment; filename=search_results.csv");
//
//Generate csv
//
echo $csvOutput
exit();
What this does is sends the text as the PHP file, but it's doesn't generate a download. What am I doing wrong?
If you are forcing a download, you can redirect the current page to the download link. Since the link will generate a download dialog, the current page (and its state) will be kept in place.
Basic approach:
$('a#query_name').click(function(){
$('#wait-animation').show();
document.location.href = '/php_scripts/utils/csv_export.php?query_name='+query_name;
$('#wait-animation').hide();
});
More complicated:
$('a#query_name').click(function(){
MyTimestamp = new Date().getTime(); // Meant to be global var
$('#wait-animation').show();
$.get('/php_scripts/utils/csv_export.php','timestamp='+MyTimestamp+'&query_name='query_name,function(){
document.location.href = '/php_scripts/utils/csv_export.php?timestamp='+MyTimestamp+'&query_name='+query_name;
$('#wait-animation').hide();
});
});
At PHP script:
#header("Last-Modified: " . #gmdate("D, d M Y H:i:s",$_GET['timestamp']) . " GMT");
#header("Content-type: text/x-csv");
// If the file is NOT requested via AJAX, force-download
if(!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') {
header("Content-Disposition: attachment; filename=search_results.csv");
}
//
//Generate csv
//
echo $csvOutput
exit();
The URL for both requests must be the same to trick the browser not to start a new download at document.location.href, but to save the copy at the cache. I'm not totally sure about it, but seems pretty promising.
EDIT I just tried this with a 10MB file and it seems that val() is too slow to insert the data. Hurrumph.
Okay, so I gave this one another go. This may or may not be completely insane! The idea is to make an AJAX request to create the data, then use the callback to insert the data into a hidden form on the current page which has an action of a third "download" page; after the insertion, the form is automatically submitted, the download page sends headers and echoes the POST, and et voila, download.
All the while, on the original page you've got an indication that the file is being prepared, and when it finishes the indicator is updated.
NOTE: this test code isn't tested extensively, and has no real security checks (or any at all) put in place. I tested it with a 1.5MB CSV file I had laying about and it was reasonably snappy.
Index.html
<a id="downloadlink" href="#">Click Me</a>
<div id="wait"></div>
<form id="hiddenform" method="POST" action="download.php">
<input type="hidden" id="filedata" name="data" value="">
</form>
test.js
$(document).ready(function(){
$("#downloadlink").click(function(){ // click the link to download
lock(); // start indicator
$.get("create.php",function(filedata){ // AJAX call returns with CSV file data
$("#filedata").val(filedata); // insert into the hidden form
unlock(); // update indicator
$("#hiddenform").submit(); // submit the form data to the download page
});
});
function lock(){
$("#wait").text("Creating File...");
}
function unlock(){
$("#wait").text("Done");
}
});
create.php
<?php
//create $data
print $data;
?>
download.php
<?php
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: text/x-csv");
header("Content-Disposition: attachment;filename=\"search_results.csv\"");
if($_POST['data']){
print $_POST['data'];
}
?>
The best way to accomplish this is to use a Data URI as follows:
Make the AJAX call to the server as per normal
Generate the CSV on the server-side
Return the data (either bare or inside a JSON structure)
Create a Data URI in Javascript using the returned data
Set window.location.href to the Data URI
See this link for instructions (paragraph #3, specifically): http://en.wikipedia.org/wiki/Data_URI_scheme
This way, you don't need to save any files on the server, and you also don't need to use iframes or hidden form elements or any such hacks.
I don't think you can make the browser download using a AJAX/JS request. Try using a hidden iframe that navigates to the page which generates the CSV
Well the point of using AJAX is to avoid a visible reload of the page. If you want a download, you want the opposite,- a brand new request from the browser. I'd say, just create a simple button pointing to your php page.
To echo and expand on what others have said, you can't really send the file using AJAX. One of the reasons for this is (and someone correct me if I'm wrong on this, please) that the page you're currently on already has sent its content headers; you can't send them again to the same window, even with an AJAX request (which is what your PHP file is attempting to do).
What I've done before in projects is to simply provide a link (with target="_blank" or javascript redirect) to a separate download PHP page. If you're using Apache, check out mod_xsendfile as well.
I have a PHP app that creates a CSV file which is forced to download using headers. Here's the relevant part of the code:
header('Content-Type: application/csv');
header("Content-length: " . filesize($NewFile));
header('Content-Disposition: attachment; filename="' . $FileName . '"');
echo $content;
exit();
What I'd like to do is redirect users to a new page after the file is built and the download prompt is sent. Just adding header("Location: /newpage") to the end didn't work, expectedly, so I'm not sure how to rig this up.
I don't think this can be done - although I am not 100% sure.
The common thing (e.g. in popular download sites) is the reverse: first you go to the after page and then the download starts.
So redirect your users to the final page that (among other things) says:
Your download should start automatically. If not click [a href="create_csv.php"]here[/a].
As about initiating the download (e.g. automatically calling create_csv.php) you have many options:
HTML: [meta http-equiv="refresh" content="5;url=http://site/create_csv.php"]
Javascript: location.href = 'http://site/create_csv.php';
iframe: [iframe src="create_csv.php"][/iframe]
very easy to do in the case it is really needed.
But you will need to have a bit work in JavaScript and cookies:
in PHP you should add setting up a cookie
header('Set-Cookie: fileLoading=true');
then on the page where you call the download you should track with JS (e.g. once per second) if there is coming cookie like that (there is used plugin jQuery cookie here):
setInterval(function(){
if ($.cookie("fileLoading")) {
// clean the cookie for future downoads
$.removeCookie("fileLoading");
//redirect
location.href = "/newpage";
}
},1000);
Now if the file starts to be downoaded JS recognizes it and redirects to the page needed after cookie is deleted.
Of course, you can tell you need browser to accept cookies, JavaScript and so on, but it works.
The header you are sending are HTTP headers. The browser takes that as a page request and processes it as a page. And in your case, a page it needs to download.
So adding a redirect header to that confuses the whole process of downloading the file (since headers are collected, generated into one header and then sent to the browser, you can try this by setting multiple redirect headers IIRC)
This is quite old issue, but here is how I achieved it via JS.
// Capture the "click" event of the link.
var link = document.getElementById("the-link");
link.addEventListener("click", function(evt) {
// Stop the link from doing what it would normally do.
evt.preventDefault();
// Open the file download in a new window. (It should just
// show a normal file dialog)
window.open(this.href, "_blank");
// Then redirect the page you are on to whatever page you
// want shown once the download has been triggered.
window.location = "/thank_you.html";
}, true);
Via - https://www.daniweb.com/web-development/php/threads/463652/page-not-redirecting-after-sending-headers-in-php
Bear in mind, however, the automatic initiation of downloadable files for IE users will trigger the security warning tab. All three of the methods outlined by daremon would show this warning. You simply can't get around this. You will be better served if you provide real links.
<?php
function force_file_download($filepath, $filename = ''){
if($filename == ''){
$filename = basename($filepath);
}
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . $filename . "\"");
readfile($filepath); // do the double-download-dance (dirty but worky)
}
force_file_download('download.txt');
?>
<script type="text/javascript">
location = 'page2.php'
</script>
Here is a solution using javascript.
I found one workaround for this that relies on javascript, so it's not exactly secure, but for non-secure critical sites it seems to work.
Have a form with a button titled 'download' with the action set to point to the download script, then using javascript put something on the onsubmit handler that strips out the download button and replaces the messaging on the screen. The download should still happen and the screen will change. Obviously, if there's an issue with the download script then it still looks like the download was successful even if it doesn't fire, but it's the best I've got right now.
Update: New Solution:
<a id="download-link"
href="https://example.com/uploads/myfile.pdf"
class="btn">Download</a>
<script>
jQuery(document).ready(function( ) {
jQuery("#download-link").click(function(e) {
e.preventDefault();
var url = jQuery(this).attr('href');
var _filename = url.split('/');
_filename = _filename[_filename.length - 1];
console.log(_filename);
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
})
.then(response => response.blob())
.then(response => {
const blob = new Blob([response], {type: 'application/pdf'});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = _filename;
document.body.appendChild(a);
a.click();
});
});
});
</script>
Source
Old answer:
Here is the answer:
It's work!
You need three different parts of code:
HTML
<a id="download_btn" class="btn btn-primary" href="?file=filename&download=1">Download<&/a>
**JQuery**
$('#download_btn').click(function(){
window.location.href = '<?=base_url()?>/?file=<?=$file;?>&download=1';
}).focusout (function(){
window.location.href = '<?=base_url()?>';
return false;
});
**PHP**
if(isset($_GET['download']) && isset($_GET['file'])){
$zip_path = 'path_to/'.$_GET['file'].'.zip';
if(file_exists($zip_path)){
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="'.basename($zip_path).'"');
header('Content-Length: ' . filesize($zip_path));
header('Location: '.$zip_path);
}
}
You can try and redirect to the URL plus a parameter that represents the file contents. And in the redirect, you can output the file content for download.
Launch the PHP file which contains the CSV download using:
<a onclick="popoutWin(\'csvexport.php\')" >Download CSV File</a>
or
<input name="newThread" type="button" value="Download CSV File"
onclick="popoutWin(\'csvexport.php\')" />
where the JavaScript function popoutWin is
/*
* Popout window that self closes for use with downloads
*/
function popoutWin(link){
var popwin = window.open(link);
window.setTimeout(function(){
popwin.close();
}, 500);
}
This will open a window, to display the CSV download prompt and then immediately close the window, leaving only the prompt.
Hmmmm...
Put the code below in the main javascript of the site.
if (window.location.pathname == '/url_of_redirect/') {
window.open(location.protocol + '//' + location.hostname + '/url_of_download.zip', '_parent');
}
Explaining:
It does the normal redirect with php to some url you will difine and in javascipt it says: If the pathname is the same as the path that redirect made '/url_of_redirect/' then it opens the url on a new page, generating the downlod.