CSV file not downloading PHP - 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

Related

How to Download Excel file in Angular using File Saver

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"
]
}
}
}

PHP headers are not working in WordPress ajax call

I have a PHP script that generate and download CSV file. Following is the code of that script:
<?php
$cars = array(
array("Volvo",22,1888),
array("BMW",15,13),
array("Saab",5,2),
array("Land Rover",17,15)
);
// output headers so that the file is downloaded rather than displayed
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=csvfile.csv');
// create a file pointer connected to the output stream
$output = fopen('php://output', 'w');
// output the column headings
fputcsv($output, array('Car', 'Year', 'Miles' ));
//Loop through the array and add to the csv
foreach ($cars as $row) {
fputcsv($output, $row);
}
exit;
?>
This script work fine when I access it directly, But my aim is to use this script in a WordPress AJAX call, I have created the AJAX using WordPress AJAX API as following:
add_action( 'wp_ajax_export_timeline', 'export_timeline' );
And then same PHP code (that is pasted above) is written in callback function export_timeline, but instead of generating CSV and downloading against the AJAX call, printed array is returned in response. There is no error in AJAX in call, I have tested with echoing other string, its responding fine.
But in case of upper mentioned script, I think PHP headers are not working in callback function, because instead of generating and downloading CSV, its echoing the array in response. Any help is appriciated.
To my knowledge PHP headers do not work with AJAX calls:
what you can do is create csv data in your php code and echo that as a response to your callback.
Your php code must return a json as
echo json_encode(['data' => $data, 'status' => 'success' ]);
$data variable must have valid CSV formatted data.
In your javascript you can use the following for CSV download:
function download_csv(){
var make_download = (function () {
var $a = $('<a>', {
'class': 'csv-downloader',
'style': 'display: none'
});
$('body').find('.csv-downloader').remove();
$('body').append($a);
return function (data, fileName) {
const blob = new Blob([data], {type: "octet/stream"}),
url = window.URL.createObjectURL(blob);
$a[0].href = url;
$a[0].download = fileName;
$a[0].click();
window.URL.revokeObjectURL(url);
};
}());
$.when($.ajax({
dataType: 'json',
method: 'POST',
url: URL, // url here
})).then((response) => {
if (response.status == 'success') {
make_download(response.data, `my-file.csv`);
}
})
}
Try to directly call the url, without an ajax call, then it should work.
Try to modify headers as below
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=csvfile.csv" );
header("Content-Transfer-Encoding: binary");

firefox cannot download csv file

I'm trying to generate and then by default download csv file using php script. I have code which is working fine in chrome and internet explorer but the same script does not works in firefox. In firefox script does not generating csv file properly. Following are the script
require('core.php');
$master = new db();
$s = $master->getRecords();
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();
}
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-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");
header("Content-Disposition: attachment;filename={$filename}");
header("Content-Transfer-Encoding: binary");
}
download_send_headers(site_title .' '. date("d M Y") . ".csv");
echo array2csv($s);
die();
HTML and JS
<a class="list-group-item" href="javascript:void(0);" onclick="return exportUser();">Export to CSV</a>
function exportUser(){
$.ajax({
url: 'get_csv.php',
type: 'POST',
success: function() {
window.location = 'get_csv.php';
}
});
}
I also attached a screenshot for understanding.
Try adding this header:
header('Content-Type: application/csv; charset=utf-8');
You need to add an event to your js function
function exportUser(event) will do it

Display message before creating excel in jquery and PHP

I am creating a web app in my company. The user can click on a button and an csv is created with MySQL data.
So far so god.
In jquery, when the user clicks the button it redirect to:
document.location.href = '/SDR/SDRJSON.php?type=' + id;
On PHP the csv file is created:
I connect to the database and create a the csv file:
while($row = $stmt->fetch(PDO::FETCH_NUM))
{
array_push($csv, $row);
}
$fp = fopen('file.csv', 'w');
foreach ($csv as $row) {
fputcsv($fp, $row, ';');
}
$FileName = 'PEW_'.$CountryCode;
fclose($fp);
header('Content-Encoding: UTF-8');
header('Content-type: text/csv; charset=UTF-8');
header("Content-Disposition: attachment; filename='".$FileName."'.csv");
header("Pragma: public");
header("Expires: 0");
echo "\xEF\xBB\xBF"; // UTF-8 BOM
readfile('file.csv');
On the page where the button is, the user clicks there and the page starts waiting for the server and then the csv file starts downloading.
For small files is ok, because it is instantaneous. But for larger files it takes like 10 / 15 seconds. Is it possible to show a message while the page waits for the server?
I don't Think PHP can echo while the csv is being made ... What you could do is split the "Document Formation" and "Document Download" into two parts.
Let Ajax Make a query for the CSV to be made . And when that has been completed the PHP (Document Formation) will echo the Path of the File.
Then After that You can use document.location.href to Newly Created File.
I ll give the code
ajax-code
$('#sample-button').click(function(){
$.ajax({
url : '/SDR/SDRJSON.php?type=' + id,
success : function(data){
if(data.url)
{
var urlToDownload = data.url;
alert("File is ready for download");
document.location.href = "http://www.domain.com/file/path/"+data.url;
// Make sure data.url has the full path or append the data.url
// with some strings to make sure the full path is reflected
// into document.location.href ...
}
else
{
alert("Something went wrong");
}
}
});
alert("CSV is being prepared Please wait... ");
});
documentFormation.php
while($row = $stmt->fetch(PDO::FETCH_NUM))
{
array_push($csv, $row);
}
$FileName = 'PEW_'.$CountryCode;
$fp = fopen($FileName, 'w');
foreach ($csv as $row) {
fputcsv($fp, $row, ';');
}
fclose($fp);
$response = array();
$response['url'] = $FileName;
echo json_encode($response); // You can handle rest of the cases where to display errors (if you have any)
// Your DocumentFormation PHP Ends here. No need for header() or readFile.
If you dont want the file to stay on server , Edit the document href to This PHP passing 'path' as the parameter
document.location.href = "documentDownload.php?path="+data.url;
documentDownload.php
$path = $_GET['path'];
$filename = end(explode("/" , $path));
header('Content-Encoding: UTF-8');
header('Content-type: text/csv; charset=UTF-8');
//Assuming $filename is like 'xyz.csv'
header("Content-Disposition: attachment; filename='".$filename);
header("Pragma: public");
header("Expires: 0");
echo "\xEF\xBB\xBF"; // UTF-8 BOM
// Reading file contents
readfile('Relative Path to File'.$path);
// Deleting after Read
unlink('Relative Path to File'.$path); // To Delete right after reading

Dojo data grid to Excel file

I have a question about exporting dojo data grid to excel file. I have made it work with csv file using the dojo exporter and some php code. However, how do I make it to save as excel file. I now about pear and some other libraries, but there has to be similar solution to the one I am using for the csv. Also, when I create my own exporter in dojo, does it need to have something more specific then the code I am using for the csv exporter. Also, what do I need to change in the php code to make it save as xls. The code is below. Thanks a lot in advance.
My dojo exporter:
function exportCsv(){
var g = dijit.byId("grid");
g.exportGrid("csv",{
writerArgs: {
separator: ","
}
}, function(str){
var form = document.createElement('form');
dojo.attr(form, 'method', 'POST');
document.body.appendChild(form);
dojo.io.iframe.send({
url: "csv.php",
form: form,
method: "POST",
content: {exp: str},
timeout: 15000
});
document.body.removeChild(form);
});
}
My php code working with csv:
<?
$time = time();
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-type: application/csv");
header("Content-Disposition: attachment; filename=\"grid_$time.csv\"");
$exportedData = $_POST['exp'];
echo stripslashes($exportedData);
exit;
?>
Here is a nice PHP tool, well suited for the purpose.
http://www.phpclasses.org/package/1919-PHP-Stream-wrapper-to-read-and-write-MS-Excel-files.html
The setup is quite simple, you have most of it setup to passthrough the .csv file as download attachment allready, try the following code.
Conversion of CSV to XLS
First setup files csv-data and classes
require_once "excel.php";
define('_CSV_SEPARATOR_', ',');
// the excel class setsup xlsfile stream writer, point it to a tmp file
$export_file = "xlsfile://tmp/example.xls";
// the csv-contents must be put into an array,
// serialized and sent to the stream
$import_file = "/path/to/CSV_FILE.csv";
$import=explode("\n", file_get_contents($import_file));
// column names should be first line
$header = array_shift($import);
Making sure, everything is looking nicely
$header = explode(_CSV_SEPARATOR_, $header);
for($i = 0; $i < count($header); $i++)
$header[$i] = trim($header[$i]);
Looping lines in remaining contents of csv-data
// rest of text is data, split em up and list them with array indices,
// and associative names as key in the rowdata
$assocData = array();
foreach($import as $line) {
$row = explode(_CSV_SEPARATOR_, $line);
$rowData = array();
$unknowncount = 0;
for($i = 0; $i < count($row); $i++) {
if(!empty($header[$i])) $column = $header[$i];
else $column = 'UNK'.$unknowncount++;
$rowData[$column] = trim($row[$i]);
}
$assocData[]=$rowData;
}
Now, we write data to the export tmp-file and conversion is done
$fp = fopen($export_file, "wb");
if (!is_resource($fp))
{
die("Cannot open $export_file");
}
fwrite($fp, serialize($assocData));
fclose($fp);
Throughputting the outputted tmp-file to client
$export_file = "xlsfile://tmp/example.xls";
header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header ("Last-Modified: " . gmdate("D,d M YH:i:s") . " GMT");
header ("Cache-Control: no-cache, must-revalidate");
header ("Pragma: no-cache");
header ("Content-type: application/x-msexcel");
header ("Content-Disposition: attachment; filename=\"" . basename($export_file) . "\"" );
header ("Content-Description: PHP/INTERBASE Generated Data" );
readfile($export_file);
exit;
Good luck and enjoy :D

Categories