I am using Yii Framework, TCPDF and jQuery to generate a pdf.
The pdf is generated by inputing in a form and submitting it using ajax.
The pdf is created but here is the problem when it returns to the client, it down not download.
here is the php code
$pdf->Output('Folder Label.pdf','D');
the jQuery on success function has
success: function(data) {
window.open(data);
}
Which i got from this site.
Can you please help
If the problem is that you are not getting the browser's download dialog for the PDF, then the solution is to do it this way:
First, redirect the browser (using window.location as the other answers say) to navigate to a special controller action in your application, e.g. with this url: http://your.application.com/download/pdf/filename.pdf.
Implement the action referenced in the URL like this:
public function actionPdf() {
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="filename.pdf";');
header('Content-Length: '.filesize('path/to/pdf'));
readfile('path/to/pdf');
Yii::app()->end();
}
This will cause the browser to download the file.
You need to save the PDF to somewhere on your server and then issue window.location = '/url/to/pdf-you-just-saved.pdf'; from your javascript. The users browser will then prompt them to download the PDF file.
in tcpdf , just pass this argument the Output method:
$pdf->Output('yourfilename.pdf', 'D');
that's all
Not quite, that will cause errors on some browsers, this is the correct way to set the window location.
window.location.assign( downloadUrlToPdf );
So
Send a request to make the pdf via Ajax to the server
Process and generate the pdf on the server
Return in the Ajax call the url to the file you just made
Use the above code fragment to open a download of said file
Related
I have created a HTML5 app that allows the user to download calendar entries in iCal format. These iCals are created using PHP. My approach until today is to generate the iCal file using PHP and writing it to hard disk on the web server. After that I pass the URL to this file to the front-end (JavaScript) so that it can be opened using the window.open() command.
This approach has the drawback that more and more files are being created on hard disk. I understood that there must be another way by adding a specific header to the response sent by the PHP iCal generator. Is this true?
I tried using these headers:
header('Content-type: application/force-download; charset=utf-8');
header('Content-Disposition: attachment; filename="eintrag.ics"');
When calling the PHP generation file directly, the iCal file is correctly displayed in the browser for being opened. But when I am calling this PHP file using AJAX from my HTML5 app, nothing happens.
What am I doing wrong?
Cheers
AJAX is "behind the scenes" communication between the user's browser and the server. Requesting data via AJAX will not display it to the user automatically, it simply delivers it to the browser. You are then responsible for acting with that data (displaying it on the page, sending it to the user, etc).
Without knowing more about your architecture, you could try something like (jQuery):
$.get('ical/', { param1: 'val', param2: 'val'}, function(response) {
// we can let the user decide to download the new ical
message = 'Your iCal is ready, click <a href="' + response.icalPath + '">here to download it';
$('.message').html(message);
// or force the user's browser to download the ical file directly
window.location.href = response.icalPath;
});
PHP:
// beep boop ... generate iCal
// return the new iCal path to the HTML5 app
return json_encode(array('success' => true, 'icalPath' => 'tmp/icals/generated.ical'));
If you really don't want to store the iCal on disk, then you can use a less flexible solution like:
window.location.href = '/ical?param=1¶m=2';
PHP would read the GET parameters, generate the iCal in memory and return with the headers you specified before.
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've got a large form where the user is allowed to input many different fields, and when they're done I need to send the contents of the form to the server, process it, and then spit out a .txt file containing the results of the processing for them to download. Now, I'm all set except for the download part. Setting the headers on the response to the jQuery .post() doesn't seem to work. Is there any other way than doing some sort of iframe trick to make this work (a la JavaScript/jQuery to download file via POST with JSON data)?
Again, I'm sending data to the server, processing it, and then would like to just echo out the result with headers to prompt a download dialog. I don't want to write the result to disk, offer that for download, and then delete the file from the server.
Don't use AJAX. There is no cross-browser way to force the browser to show a save-as dialog in JavaScript for some arbitrary blob of data received from the server via AJAX. If you want the browser to interpret the results of a HTTP POST request (in this case, offering a download dialog) then don't issue the request via AJAX.
If you need to perform some kind of validation via AJAX, you'll have to do a two step process where your validation occurs via AJAX, and then the download is started by redirecting the browser to the URL where the .txt file can be found.
Found this thread while struggling with similar issue. Here's the workaround I ended up using:
$.post('genFile.php', {data : data}, function(url) {
$("body").append("<iframe src='download.php?url="+url+"' style='display: none;'></iframe>");
});
genFile.php creates the file in staging location using a randomly generated string for filename.
download.php reads the generated file, sets the MIME type and disposition (allowing to prompt using a predefined name instead of the random string in the actual filename), returns the file content and cleans up by deleting the source file.
[edit] might as well share the PHP code...
download.php:
<?php
$fname = "/tmp/".$_GET['url'];
header('Content-Type: text/xml');
header('Content-Disposition: attachment; filename="plan.xml"');
echo file_get_contents($fname);
unlink ($fname);
?>
genFile.php:
<?php
$length = 12;
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$str = substr( str_shuffle( $chars ), 0, $length ).'.xml';
$fh = fopen(('tmp/'.$str), 'w') or die("can't open file");
fwrite($fh,$_POST["data"]);
fclose($fh);
echo $str;
?>
Rather than using jQuery's .post(), you should just do a normal POST by submitting the form, and have the server respond with appropriate Content-Encoding and MIME-type headers. You can't trigger a download through post() because jQuery encapsulates the returned data.
One thing I see in use rather frequently, though, is this:
$.post('generateFile.php', function(data) {
// generateFile builds data and stores it in a
// temporary location on the server, and returns
// the URL to the requester.
// For example, http://mysite.com/getFile.php?id=12345
// Open a new window to the returned URL which
// should prompt a download, assuming the server
// is sending the correct headers:
window.open(data);
});
I want to write a php script to download some files(extensions - .apk, .dcm, .pdf, .zip etc...) on mobile browsers. I have written a php code to download those files and it is working fine on all the browsers(not mobile browsers). But I tried it using a HTC mobile and it is trying to open the file instead of downloading(like opening a web page).
How can I enable the download on mobile browsers?
Thank You
PS:
This is how I do it.
I use a jquery code to send some parameters to a php file and it will return the appropriate download file path.
jQuery code snippets is:
$.post('saveData.php',{ name: name.val(), email: email.val(), phone:phone.val(), address:address.val(), version:vers, swVersion:swvers, type:type },
function(data)
{
var links = data;
document.body.innerHTML += "<iframe src='" + links + "' style='display:hide;' ></iframe>";
});
"links" variable contains the download path return from the php file.
Iframe allow the download window to popup. But it does not work on the mobile browsers.
Try with the following headers:
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename="filename"');
The idea is to set the content type to something that the browser doesn't know how to open - that way it will show the save dialogue. You can try it with the actual MIME type, it should work, but I can't test it right now.
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.