How to Download Excel file in Angular using File Saver - php

I had created post form in PHP where on clicking the button it was downloading an excel file and I had some form data also posted to the URL and file used to download successfully with plain HTML and submit form
In PHP this is function triggered when the form is posted to the file
public function downloadExcel($fileName = '', $addTimeStamp = true) {
$fileName = (!$fileName) ? 'excel_download' : preg_replace('/\s+/', '_', $fileName);
if ($addTimeStamp) {
$fileName .= date('_d_m_Y_H_i_s');
}
$fileName .= '.xlsx';
$this->setActiveSheetIndex(0);
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="' . $fileName . '"');
header('Cache-Control: max-age=0');
header('Cache-Control: max-age=1');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: cache, must-revalidate');
header('Pragma: public');
$objWriter = PHPExcel_IOFactory::createWriter($this, 'Excel2007');
$objWriter->save('php://output');
}
When it was working, I have below thing set in the Request Headers
And it was nothing showing in the response
But now we are trying to migrate frontend to the Angular framework from which we are able to download the file, I have tried his way
downloadTheExport() {
this.downloadfile().subscribe((blob: any) => {
const blobdownload = new Blob([blob], { type: "application/vnd.ms-excel;charset=utf-8" });
saveAs(blobdownload, 'testdata.xls');
});
}
downloadfile(){
const formDataForExport: FormData = new FormData();
formDataForExport.append('export', 'ALL');
return this.http.post('http://localhost:8080/service/exportExcel.php', formDataForExport, {
headers: { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' }
});
}
And When I am trying to download this in Angular I have observed that when the Call is made in Angular it seems Request header for Content-Type is changed to Content-Type: multipart/form-data; boundary angular and also when I see in the response tab it showing some data as below.
Can you please help me how to achieve downloading in Angular in this situation

It is correct for the Content-Type in your Request header since you indeed post a formDataForExport via php backend.
But the way to handle the response seems wrong. I propose a solution below may help:
Assuming if you are referencing this fileSaver:
https://github.com/Hipparch/file-saver-typescript/blob/master/file-saver.ts
Recommend to include above script and use it since to handle different browser behaviour in saving files it is in complexity and not good for re-implement them.
downloadTheExport() {
this.downloadfile().subscribe((resp: any) => {
const fileSaver: any = new FileSaver();
fileSaver.responseData = resp.body;
fileSaver.strFileName = 'testdata.xls';
fileSaver.strMimeType = 'application/vnd.ms-excel;charset=utf-8';
fileSaver.initSaveFile();
});
}
downloadfile() {
const formDataForExport: FormData = new FormData();
formDataForExport.append('export', 'ALL');
return this.http.post('http://localhost:8080/service/exportExcel.php', formDataForExport, {
headers: { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' },
responseType: 'blob',
observe: 'response'
});
}

If anyone wants only to download a file from assets, they can use this code:
But you need to mention the location in angular.json
"architect": {
"build": {
"options": {
"assets": [
"src/assets/files"
]
}
}
}

Related

Download file via PHP

I'm trying to download a zip file without revealing the URL to the user. The file should only be accessible by authorized users.
Currently I'm doing this:
// check authentication and request method etc...
// ...
$file = '../../filename.zip';
$filesize = filesize($file);
header('Content-Description: File Transfer');
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: private');
header('Content-Length: ' . $filesize);
ob_clean();
flush();
echo readfile($file);
die();
And I can see the file is downloaded somehow in the Chrome network tab (it shows in the Response tab) and ~3MB are transferred (which is the filesize), but the file isn't downloaded in the browser.
I'm doing a simple GET request, btw.
Thanks for your help,
Tobias
EDIT: Okay, apparently I also had an error on the client side and now the file is being downloaded in the browser, but it's only a 1KB zip containing Response with status: 200 OK for URL: xxx.
My request looks like this:
let headers = new Headers({'Accept': 'application/zip'});
let options = new RequestOptions({ headers: headers, responseType: ResponseContentType.Blob });
this.authHttp.post(environment.apiUrl + 'rewards/download/', data, options)
.map(res => new Blob([res],{ type: 'application/zip' }))
.catch((error:any) => Observable.throw(error.json().error || 'Server error'))
.subscribe(
data => {
//console.log(data);
window.location.href = window.URL.createObjectURL(data);
},
error => {
console.log("Error downloading file.");
},
() => console.log("Download completed."));
EDIT2: Okay, got it. Apparently I had to use .map(res => new Blob([res.json()],{ type: 'application/zip' })) in the request. Also now I'm using FileSaver.js instead of window.location, but I don't think that affected the problem.
i think you just need to redirect ur method to your physique zip file.
i mean you need just to add :
header("Location: $url_physique_of_ur_zip_file");
before
ob_clean();
flush();
and replace $url_physique_of_ur_zip_file by your physique url of your file
i wish u understand me because this solution work for me
Okay, got it. Apparently I had to use .map(res => new Blob([res.json()],{ type: 'application/zip' })) in the request. Also now I'm using FileSaver.js instead of window.location, but I don't think that affected the problem.

CSV file not downloading PHP

I am trying to export and download a csv file through php. I have done exactly what was suggested in Export to CSV via PHP
I can see my array dump in the response but the csv file is just not downloading. Please Help.
Here is my code:
function download_send_headers($filename) {
// disable caching
$now = gmdate("D, d M Y H:i:s");
header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
header("Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate");
header("Last-Modified: {$now} GMT");
// force download
Header('Content-Description: File Transfer');
header("Content-Type: application/force-download");
// header("Content-Type: application/octet-stream");
// header("Content-Type: application/download");
// disposition / encoding on response body
header("Content-Disposition: attachment;filename={$filename}");
header("Content-Transfer-Encoding: binary");
}
function array2csv(array &$array)
{
// if (count($array) == 0) {
// return null;
// }
ob_start();
$df = fopen("php://output", 'w');
fputcsv($df, array_keys(reset($array)));
foreach ($array as $row) {
fputcsv($df, $row);
}
fclose($df);
return ob_get_clean();
}
Here is how im using it:
download_send_headers("data_export_" . date("Y-m-d") . ".csv");
echo array2csv($modsucc);
die();
This is javascript function:
function exporttocsv(filter){
var fd = new FormData();
fd.append('filter', filter);
fd.append("form", "export_to_csv");
$.ajax({
url: getBaseURL()+'assets/handler/OrderManagementHandler.php',
type: 'POST',
data: fd,
enctype: 'multipart/form-data',
processData: false,
contentType: false,
})
.done(function(res) {
})
.fail(function() {
});
}
Handler:
case 'export_to_csv':
$controller->exportToCSV($_POST);
break;
Controller:
public function exportToCSV($data){
$filter = $data['filter'];
$mod = new OrderManagementModel();
$modsucc = $mod->exportToCSV($filter);
if($modsucc){
// var_dump(ini_get('output_buffering'));
//var_dump($modsucc);
download_send_headers("data_export_" . date("Y-m-d") . ".csv");
echo array2csv($modsucc);
die();
}
}
Your code not work because you use ajax and you cant download files with ajax itself, simple way is this:
...
if($modsucc){
$file = /* directory */"data_export_" . date("Y-m-d") . ".csv";
$df = fopen(file, 'w');
fputcsv($df, array_keys(reset($array)));
foreach ($array as $row) {
fputcsv($df, $row);
}
fclose($df);
echo $file;
}
...
this will save file, and in your ajax done function:
window.open(res);
this will open new window with address to previously saved file or
window.location.href = res;
this will redirect you to address where the file was saved
to force download you could do it like this:
//force-download.php
if(file_exists($_GET['file'])){
download_send_headers("data_export_" . date("Y-m-d") . ".csv");
echo file_get_contents($_GET['file']); // warning: unsafe !! session for example will be better
}
this will send headers for force download and read data from disk where data was previosly saved and echo them
and in your ajax done function:
window.open('force-download.php?file=' + res);
or
window.location.href = 'force-download.php?file=' + res;
this use address where force download headers will be sent
Another possibility is, change $_POST to $_GET and instead of using ajax just redirect to url and it will work with your old code
Your code work, only think what could be wrong is if your server dont have enabled output buffering and you output something before calling function download_send_headers

$_POST data to phpExcel

I am trying to export data from a html table to an excel file using phpExcel. The issue I am having is that I cannot seem to get the data through to it.
I have the following jquery code which runs on a button press to get the data out of the table and post it to the php code.
export data jquery
function storeTblValues()
{
var tableData = new Array();
$('#LogsTable tr').each(function(row, tr)
{
if(row == 0) //Table Headers
{
tableData[row] =
{
"LogDate" : $(tr).find('th:eq(0)').text(),
"LogType" : $(tr).find('th:eq(1)').text(),
"StartTime" : $(tr).find('th:eq(2)').text(),
"FinishTime" : $(tr).find('th:eq(3)').text(),
"Duration" : $(tr).find('th:eq(4)').text()
}
}
else //Table data
{
tableData[row] =
{
"LogDate" : $(tr).find('td:eq(0)').text(),
"LogType" : $(tr).find('td:eq(1)').text(),
"StartTime" : $(tr).find('td:eq(2)').text(),
"FinishTime" : $(tr).find('td:eq(3)').text(),
"Duration" : $(tr).find('td:eq(4)').text(),
}
}
});
return tableData;
}
function exportToExcel()
{
var tableData;
tableData = $.toJSON(storeTblValues());
var tmp = "pTableData=" + tableData;
$.ajax({
type: 'POST',
url: 'create_export_files.php',
data : tmp,
success : function(data)
{
window.location = 'create_export_files.php';
}
});
}
Top bit of the php file receiving the data
Create_export_files.php
$tableData = "";
if (isset($_POST["pTableData"]))
{
$tableData = $_POST["pTableData"];
$tableData = json_decode($tableData, TRUE);
}
else
{
$tableData = "empty";
}
generateExcel($tableData);
function generateExcel($tableData)
{
/** Include PHPExcel */
require_once dirname(__FILE__) . '/../Classes/PHPExcel.php';
// Create new PHPExcel object
$objPHPExcel = new PHPExcel();
// Add some data
$objPHPExcel->setActiveSheetIndex(0)
->setCellValue('A1', 'hello')
->setCellValue('B2', 'world!')
->setCellValue('C1', 'Hello')
->setCellValue('D2', 'world!');
// Rename worksheet
$objPHPExcel->getActiveSheet()->setTitle('Simple');
// Set active sheet index to the first sheet, so Excel opens this as the first sheet
$objPHPExcel->setActiveSheetIndex(0);
$filename = 'export '.date('d-m-Y h:i:a').'.xlsx';
// Redirect output to a client’s web browser (Excel2007)
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="'.$filename.'"');
header('Cache-Control: max-age=0');
// If you're serving to IE 9, then the following may be needed
header('Cache-Control: max-age=1');
// If you're serving to IE over SSL, then the following may be needed
header ('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
header ('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); // always modified
header ('Cache-Control: cache, must-revalidate'); // HTTP/1.1
header ('Pragma: public'); // HTTP/1.0
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
$objWriter->save('php://output');
}
I always seem to be going into the else part of the statement. Where am I going wrong?
If I remove the isset bit around the pTableData then the file gets created but is corrupted. Opening it in notepad++ I can see it says pTableData is undefined. Is that because the php file gets run again to make the file download?
try this code:
<?php
$str = "<table>
<tr><td>a</td><td>b</td></tr>
<tr><td>a</td><td>b</td></tr>
<tr><td>a</td><td>b</td></tr>
<tr><td>a</td><td>b</td></tr>
</table>
";
file_put_contents("table.xls",$str);

File corruption on downloading docx using phpWord - javascript client side issue

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.

Force Download via Ajax and PHP

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);

Categories