This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Downloading Via JQuery AJAX Post not working
filedownload.php has below snippent.
$file = 'cut.png';
header("Content-Type: image/png");
header('Content-Disposition: attachment; filename="'.$file.'"');
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
readfile($file);
exit();
AJAX Call
jQuery.post('filedownload.php',{
'file' : result // not used for the time being
});
I make an ajax call to the filedownload.php file. It does not allow user to download the file. But If I run the php directly it allows user to download the file. What could be the issue ?
I would like to use core functions rather than using jQuery Plugins. If it's not possible a plugin would be fine.
Given that I use ajax because the page can not be refreshed.
The Problem
Let's take the example of a productivity web app such as a spreadsheet
editor, which has the ability to open, save, import and export. The
open and save options would involve loading a spreadsheet from the
database, whereas import and export deal with local files on the
user's machine. To implement the export behavior, you might decide
that the user should have to save their spreadsheet first, allowing
you to export the data from the backend to file. But let's assume
instead you'd like to allow users to export their data without saving,
perhaps to afford them the option of working locally without ever
storing data on the server. In order to do this, you'd need to send
the current spreadsheet data to the backend and receive a file to
download. Unfortunately, this can not be handled using Ajax, since
Ajax can only receive responses in the form of text. In cases where
the data to be saved is rather lengthy, this poses a considerable
problem.
The Workaround
In order to make the request, you'd need to make a regular (not Ajax)
HTTP request using GET or POST. If the data is reasonably short, you
might get away with a GET request (perhaps by simply setting
Window.location to your export url), but due to varying browser
limitations on GET request length, a POST will most likely be needed.
The following plugin allows you to make a request that returns a file
in a similar syntax to jQuery's native Ajax functions.
jQuery Code Which fixes the problem
jQuery.download = function(url, data, method){
//url and data options required
if( url && data ){
//data can be string of parameters or array/object
data = typeof data == 'string' ? data : jQuery.param(data);
//split params into form inputs
var inputs = '';
jQuery.each(data.split('&'), function(){
var pair = this.split('=');
inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
});
//send request
jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
.appendTo('body').submit().remove();
};
};
How to call
$.download('filedownload.php','filename='+filename );
Read more
Related
So my application generates pdf files using TCPDF, and that works fine. That is done inside php file, called with ajax. I'm using embed tag to preview them like:
$.ajax({
url: 'create_pdf_file.php',
success: function(){
/* https://stackoverflow.com/questions/17083018/jquery-loads-cached-file-inside-ajax-success-function */
$('#pdf_placeholder embed').attr('src','output/my_file.pdf?v=' + Math.random()');
}
});
Because many users could generate my_file.pdf at the same time, there could be a case when one user will preview a file generated for another user. So my question at this point is how to force TCPDF to output directly into that tag, not using temp file
Output('my_file','I')
not working here after ajax.
There is an advice here to echo pdf directly back, but i don't know how to do that or is it possible at all. Anyway will try with success(data) to receive that like json.
Other workaround is to give session-bond file name for each user, but is using session_id() and appending to the file name is safe? Most probably I will end with generating UID for each session.
Any general advices are welcome.
You should try passing what TCPDF will produce in a way widely used in embeding images: Data Uri
Embedding Base64 Images
This however might choke browser - i haven't test this.
I would rather save pdf file on server, print filename to browser or other ID of produced file so it could be read with "success" of the ajax request. Then calmly pass proper filename to Embed element. If you wish to do it more safely you can encode it using already used session-exclusive data like session cookie or data that is assosiated with that cookie on a server. You can bond pdf file access to IP that sent request to produce it and timestamp of request that caused production.
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 button. By clicking the button, I want to export some data shown on the webpage to a file for downloading.
I am doing this in this way: I have a export.php. I send the data as the parameter to PHP file (another parameter is filename), and PHP server create a file and write the data, and then send file. Code is like:
$filename = $_GET['filename'] . '.csv';
$export = $_GET['export'];
$writer = fopen($filename, 'w') or die('cannot create');
fwrite($writer, $export . "\n");
fclose($writer);
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename='.basename($filename));
readfile($filename);
unlink($filename);
exit();
For cases that the data are short, it works fine. But if the data are long, since the data are passed as part of the URL, I will get "Request-URI Too Large" error.
Is there any alternative way to do that? Is it possible to directly write data using JavaScript?
It sounds like you are sending to export.php with a GET, when you should be using a POST. GET is limited to 2048 characters, while a POST is not limited.
You'll need to POST the data to the server. Change the METHOD in your FORM tag to POST (in your html not your php code).
Each browser limits the size of the query string / length of the URL and the limit is browser dependent. You can POST a very large amount of data to the server however. The only limit is how fast is the user's upstream bandwidth and their patience.
Instead of passing the data as a query string, use javascript to create an iframe and build a form which then posts to the php file.
IF your using jquery there's a good tutorial http://tutorialzine.com/2011/05/generating-files-javascript-php/
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'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.