Download a file with an ajax call - php

I am using PHPExcel to read an excel template, populate the data, and ask the user to download the file.
generate_excel.php
$objPHPExcel = PHPExcel_IOFactory::load("./template.xlsx");
//populate data ...
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="01simple.xlsx"');
header('Cache-Control: max-age=0');
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
$objWriter->save('php://output');
When I open generate_excel.php directly from the browser, the result file is downloaded.
But if I make an ajax call to the generate_excel.php, I don't get the download prompt. Using chrome developer tools, I can see from the Network tab that the ajax call was successfully completed and a bunch of random characters is seen in the response data. I'm assuming that is the excel object.
Does anyone know how I can achieve the download excel feature using ajax? I don't want to refresh the page. When the user clicks on the "export" button, there should be an ajax call to the php file and prompt the user to download.
Thanks!

I looked for ways to pass JSON data with ajax to PHP and return an excel file (MySQL and PHPExcel) for the user to save.
I looked around and put some pieces together, hope it can help someone:
jQuery:
$("#exportBotton").on("click",function(event) {
event.preventDefault();
// create json object;
str_json = JSON.stringify({"key01":val01, "key02":val02, "key03":val03});
$.ajax({
type: "post",
data: str_json,
url: "../../includes/dbSelect_agentFormExport.php",
dataType: "json",
success: function(output){
// output returned value from PHP t
document.location.href =(output.url);
}
});
});
PHP:
$str_json = file_get_contents('php://input');
$objPHPExcel = new PHPExcel();
// here i populated objPHPExcel with mysql query result.....
function saveExcelToLocalFile($objWriter){
// make sure you have permission to write to directory
$filePath = '../tmp/saved_File.xlsx';
$objWriter->save($filePath);
return $filePath;
}
$objWriter = new PHPExcel_Writer_Excel2007($objPHPExcel);
$response = array(
'success' => true,
'url' => saveExcelToLocalFile($objWriter)
);
echo json_encode($response);
exit();

Not everything should be done with AJAX. Sometimes plain old HTML is more suitable for a job. I guess your button has a tag? Why won't you do something like this
Export to Excel
in your HTML? Note the target="_blank" part. It's there to make sure your page is not reloaded.
For input you can use construct
<form action="generate_excel.php" target="_blank"><input type="button">...whatever</form>

Here is an example of how to download a file using an AJAX call:
var xhr = new XMLHttpRequest();
xhr.open("GET", "path/to/file.ext", true);
xhr.responseType = "blob";
xhr.onload = function(e) {
if (xhr.status === 200) {
var a = document.createElement("a");
a.href = URL.createObjectURL(xhr.response);
a.download = "file.ext";
a.style.display = "none";
document.body.appendChild(a);
a.click();
}
};
xhr.send();

Found a way to do this, although I'm not sure if this is an ideal approach.
I added a hidden iframe in the page. When the ajax call returns, it returns the url of the created data. I used javascript to redirect the iframe to that url which automatically triggers the download action.

You can try this way:
Send a Jquery AJAX POST request with the data that is to be used to generate excel report, and store that data in a session variable. Return an arbitrary string like 'success' as the response.
If the output of the above AJAX call is 'success', then do a GET request to another URL in your application, that reads the data from session (stored in the first step, else throw an error), prepares an excel file out of that data, and forces the download of that excel file to the browser.

Related

JQuery and PHP: is it possible to download a file via an AJAX call?

I've got a web page that displays data in a table and what I want to add is a button that the user can click to download the table as an Excel file. I use PHPExcel to make the Excel file, and it seems to work with no errors until I actually get to downloading the file - it seems the browser generates the file just fine, but doesn't actually download the finished file.
I did take a look at this question here: phpexcel to download, but I tried what the answers said to do and it didn't seem to change anything.
Here's my frontend code ("table.php"):
<script>
$("#export-to-excel").click(function() {
$.ajax({
type: "GET",
url: "table_create_excel.php",
data: {
action: "exportToExcel",
tableRows: rows
}
});
}
</script>
<img src='excel-icon.png' id="export-to-excel"/>
Here's my backend code ("table_create_excel.php"):
require_once "PHPExcel.php";
$objPHPExcel = new PHPExcel();
// ...
// ... generate the excel document from the given data ...
// ...
$objPHPExcel->setActiveSheetIndex(0);
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment;filename="Table_Summary.xlsx"');
header('Cache-Control: max-age=0');
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
$objWriter->save('php://output');
Is there something I'm missing that, if I could put it in, would get my browser to download the file? Could the trouble be because I'm making an AJAX call rather than linking to the PHP page directly? Thanks.
Turns out my suspicion was correct, and that you can't use AJAX to download a file. What I did was got rid of the JQuery "click" event and replaced it with an anchor tag around the "export to Excel" icon, which had a link to the PHP file that generated and downloaded the Excel file along with enough parameters to allow the PHP code to re-query the database and rebuild the table that I wanted to export rather than including the table in the data that was sent.

jQuery using AJAX to display PDF data obtained from a PHP file

I am trying to use AJAX to query a PHP file and display a PDF file to the user. The response from the PHP file is the raw data of a PDF file stored on my server. Below is the code I am using to try and accomplish this but it isn't working. I keep getting a bad request error from my browser. Does anyone know the right way of doing this?
My end goal is I do not want the user to be able to see the server path where I store my PDF files. I only want the PDF files to be accessible using the AJAX / PHP script. I understand it's not the most secure method but I just want to keep the layman away from my PDF files.
jQuery:
$.ajax({
type: "POST",
url: 'process.php',
data: {"name" : "value"},
success: function (data) {
var json = $.parseJSON(data);
if(json.hasOwnProperty('success')){
window.location(json.success);
// json.success should contain the pdf binary data
// i just cant figure out how display the pdf in the browser
}
}
});
PHP:
<?php
$fileName = $_POST['name'];
if(isset($fileName)){
$file = './path-to-forms/'.$fileName.'.pdf';
$pdfData = file_get_contents($file);
$data = array("success" => $pdfData, "name" => $fileName);
echo json_encode($data);
}
?>
Does anyone know the right way of doing this?
A couple changes should get the file downloading correctly:
Update the PHP code to send the file contents using base-64 encoding (i.e. with base64_encode()):
$data = array("success" => base64_encode($pdfData));
When the AJAX response completes, create an anchor (link) and simulate clicking on it using .click() to initiate the PDF download. I can't find any jQuery method window.location() on api.jquery.com... if you find it, let me know. Maybe you were thinking of updating the (read-only) property window.location?
var json = $.parseJSON(data);
if(json.hasOwnProperty('success')){
var a = document.createElement("a");
a.href = 'data:application/pdf;base64,'+json.success;
a.download = "filePDF"; //update for filename
document.body.appendChild(a);
a.click();
// remove `a` following `Save As` dialog,
// `window` regains `focus`
window.onfocus = function () {
document.body.removeChild(a)
}
}
Credit to guest271314 for the adapted code from this answer along with some of the code from Alexandre's code in the answer below that.
See it demonstrated in this phpfiddle.

PHP YII2 - is it possible to direct to certain page after download file? [duplicate]

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.

send pdf to browser after ajax call

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&param2=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.

PHP generate file for download then redirect

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.

Categories