exporting a csv file in PHP and JavaScript - php

I'm trying to implement a tool to export data from a research website as a csv file. In my download controller, I have:
public function export(){
$data = array(
array('Name', 'Age', 'City'),
array('Philippe', '23', 'Palo Alto'),
array('John', '30', 'San Francisco'),
array('Paul', '20', 'Los Angeles')
);
header('Content-type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=data.csv');
$fp = fopen('php://output', 'w');
foreach( $data as $line ) {
fputcsv( $fp, $line );
}
fclose($fp);
}
and from my view, I do:
function download() {
$('#download').empty().html('<img src="<?php echo base_url();?>/images/loader.gif" />');
$.post('<?php echo base_url();?>/index.php/download/export');
}
and download() is fired up when the user click to a button with a onclick='download()'.
However, I'm not seen the file being downloaded, nor a error message. When I look into my firebug console, I see:
200 OK 29ms jquery.min.js (line 4) HeadersPostResponseCookies
Name,Age,City Philippe,23,"Palo Alto" John,30,"San Francisco"
Paul,20,"Los Angeles"
What am I missing?
Thanks

You don't actually want an AJAX request here. You want to redirect your user to the file with:
window.location = '<?php echo base_url();?>/index.php/download/export';
The AJAX request is telling the server to send the data back to the script's success method. Since it's a download, the redirect won't make you leave the page so you can use this method without confusing your user.

Related

Email attachement in Laravel

I am working on Laravel file attachment. I have to send CSV file as attachment in mail without downloading the file to my server. When I click the submit button it didnot send email with file attachment.
My Controller:
$data["email"] = $request->recipient_email;
$data["subject"] = 'Cashup Report for '. $venue->name;
$data["bodyMessage"] = $venue->name.' from '.$start.' to '.$end ;
$excel_file = Excel::create(uniqid().'Cashups', function($excel) use($transactions,$start,$end,$venue) {
$excel->sheet('New sheet', function($sheet) use($transactions,$start,$end,$venue) {
$sheet->loadView('excel.cashups', array('transactions' => $transactions, 'start'=> $start, 'end' => $end, 'venue' => $venue));
});
});
//Feedback mail to client
Mail::send('emails.cashups_report', $data, function($message) use ($data,$excel_file){
$message->from(config('mail.from.address'));
$message->to($data["email"]);
$message->subject($data["subject"]);
//Attach PDF doc
$message->attachData($excel_file,'cashups-report.xlsx');
});
I dont know where i am going wrong. I have already put much time over this but didn't find any solution.
Any guidence in this regards will be highly appreciated. Thanks
You can achieve your goal using the code below. I tried my level best to explain everything in the comments of the code.
//function to generate the csv to be attached in your email
public function createCSV()
{
$myData = MyModel::where(myConditions)->get(['col1','col2','col3']);
header('Content-Type: text/csv; charset=utf-8');
//header without attachment; this instructs the function not to download the csv
header("Content-Disposition: filename=myCsvFile.csv");
//Temporarily open a file and store it in a temp file using php's wrapper function php://temp. You can also use php://memory but I prefered temp.
$Myfile = fopen('php://temp', 'w');
//state headers / column names for the csv
$headers = array('col_name1','col_name2','col_name3');
//write the headers to the opened file
fputcsv($Myfile, $headers);
//parse data to get rows
foreach ($myData as $data) {
$row=array(
$data->col1,
$data->col2,
$data->col3,
);
//write the data to the opened file;
fputcsv($Myfile, $row);
}
//rewind is a php function that sets the pointer at begining of the file to handle the streams of data
rewind($Myfile);
//stream the data to Myfile
return stream_get_contents($Myfile);
}
second function: this sends the email to receipients with the csv attached
public function sendEmail()
{
Mail::send('path_to_your_view.My_view', array(''),
function($message){
$message->to(explode(',', env('EMAILS')))
->subject('Email Subject')
->attachData($this->createCSV(), "MyfileName.csv");
});
}
/*......¯\_(ツ)_/¯......
It works on my computer*/
Best of luck! :)

Echoing to screen when using headers to output to csv

I have a PHP application which generates a set of codes , saves them to MySQL DB and then outputs the same to the user as a downloadable csv file. I also have an echo statement after the code block to convert the PHP array to csv. The echo statement after the convert_to_csv function call instead of outputting to the browser outputs to the file instead and overwrites the first line. How do I get the echo statement to output to the browser instead? The code block is below:
convert_to_csv($newCodesArray,$fileName,',');
echo "Your file was successfully generated";
function convert_to_csv($input_array, $fileName, $delimiter)
{
header('Content-Type: text/csv');
header("Content-Disposition: attachment; filename=\"$fileName\"");
$f = fopen('php://output', 'w');
/* loop through array */
foreach ($input_array as $line) {
/* default php csv handler */
fputcsv($f, $line, $delimiter);
}
fclose($f) or die("Can't close php://output");
}
You have already defined the header as text/csv. So it wont print in the browser as it requires text/html.
Alternatively you can do as following. Copy your function to different file (Ex. csv.php).
<?php
echo "Your file was successfully generated <script> window.location = 'csv.php' </script>";
Now it will print your echo string and start download your csv file.
As Magnus Eriksson commented,
Above code does not checking its really generated successfully or not. We can extend code with AJAX.
<script>
$.ajax('csv.php', {
success: function(data) {
document.write('Your file was successfully generated.');
windows.location = 'csv.php';
},
error: function() {
document.write('Your file generation failed.');
}
});
</script>
Note:- AJAX call will generate file two times.

Why PHP continue to output to CSV file after fclose()?

On a web page, I am writing some data into a CSV file using the below code and finally closing with fclose();
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename='.$filename);
$out = fopen('php://output', 'w');
fputcsv($out, $cvs_cols);
fclose($out);
echo "HELLO WORLD"; // sneaks into CSV!?
Why is it that "HELLO WORLD" gets into the CSV download file when it has already fclose()? I want to output the rest of the HTML for the page to be displayed in the browser. How can I do that?
After 1 HTTP request follows 1 response. You cannot send content type text/csv and content type text/html at the same time (maybe yes with SPDY, but not with pure HTTP).
fclose closes your file descriptor but not the output to the browser.
You should also set a Content-Length header and put in the filesize.
Mark Baker already gave the most important point in the comments:
echo and writing to php://output puts content into the same stream: STDOUT. Other options would be to write the CSV to memory (but its senseless if you don't use it) or to a file. Read more about the those streams: http://www.php.net/manual/en/features.commandline.io-streams.php
Possible solution:
You need 2 HTTP requests. 1 For the download, the other for your HTML. Most popular way is it to first use the HTML response and put something in like
<meta http-equiv="refresh"
content="3; URL=http://yourserver.com/download.php?id=pdf&id=123" />
This starts the download after 3 seconds.
There is no 'CSV File' (yet).
What you are doing is sending a data stream to the client, and telling the client that this stream has a Content-Type of text/csv and a filename of $filename. The client can then chose to save this as a CSV file or just display it in the browser.
This code:
$out = fopen('php://output', 'w');
fputcsv($out, $cvs_cols);
fclose($out);
Is effectively doing the same thing that echo $cvs_cols would do (with a little extra stuff to format a csv output).
So when there is a call to echo "HELLO WORLD"; it gets sent in the same data steam as the contents of the $cvs_cols variable.
When you call fopen('php://output', 'w') you are creating a second file handle to php://output as one is created by default to output from calls to echo etc. So when you are calling fclose($out) you're only closing the second file handle.
A very old thread here but to fix this I just added a simple exit(); command. So a button calls the same page with a query string of 'action=export_csv' then that action is run with the exit(); on the last line, hope that helps out.
Export CSV
Then the 'action' on the page is:
if(isset($_GET['action']) && $_GET['action']=='export_csv'){
// output headers so that the file is downloaded rather than displayed
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=email-responses.csv');
// create a file pointer connected to the output stream
$output = fopen('php://output', 'w');
// output the column headings
fputcsv($output, array('Email address'));
$db = new PDO('mysql:host=hostname_mysql;dbname=database_mysql;charset=UTF8', username_mysql, password_mysql);
$query = "SELECT XXX FROM XXXX";
$result = $db->query($query);
$data = $result->fetchAll(PDO::FETCH_ASSOC);
// loop over the rows, outputting them
foreach($data as $row){
fputcsv($output, $row);
}
fclose($output);
exit();
}
use
ob_clean() : ob_clean — Clean (erase) the output buffer
flush() : flush — Flush the output buffer(flush)
ob_start();
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename='.$filename);
$out = fopen('php://output', 'w');
fputcsv($out, $cvs_cols);
fclose($out);
ob_end_clean(); // the buffer and never prints or returns anything.
echo "HELLO WORLD"; // sneaks into CSV!?

Write to file and send AJAX response at once

UPDATE:
I used another solution to write my data into a file. It seems that I can't echo data while AJAX is waiting for a response. So I now use fwrite.
$fileHandle = '';
$fileHandle = fopen("export.txt","w");
fwrite($fileHandle, $export);
Original:
Hi there,
maybe my logic is wrong.
I make an AJAX call to get data from another URL.
That worked so far.
But now I want to add an file export also.
$handler = new MyHandler();
// Step 1: get data from URL
$dataAjax = $handler->getData($_POST['data']);
// Step 2: write the data into a text file to provide a download
$handler->writeToText($dataAjax);
echo json_encode($dataAjax);
Now the console shows me a "parserError" because my JSON data contains also the string I wanted to write into the file. That's bad and unwanted.
This below is just a test how I want to write my data into a txt file:
function writeToText($data)
{
header("Content-type: text/plain");
header("Content-Disposition: attachment; filename=export.txt");
header("Pragma: no-cache");
header("Expires: 0");
$title = "";
$title .= "Name,Quantity,Model,Price,Weight,Status"."\n";
echo $title;
}
That is how the error looks like:
{
"readyState": 4,
"responseText": "Name,Quantity,Model,Price,Weight,Status\n[{\"domain\":\"Text\",\"name\":\"Banana\}]",
"status": 200,
"statusText": "OK"
}
parsererror

Download CSV file in jquery mobile framewrok with zend

I am doing project with jquery mobile framework using zend framework.I have the page to download report in csv format. I can downloaded it from desktop version. But the same page in mobile version runs in ajax due to jquery mobile result undefined with blank page.
for example, i call this http://www.test.com/report/downloadtransaction when i click download button. This calls downloadtransactionAction in reportcontroller ,then finally gives csv file.
Call is happening in mobile version but not able to see download file window.
Below is the code
public function downloadtransactionAction(){
$preview_result = getmySessionData('preview_result')->preview_key;
//print_obj($preview_result);
$myFile = "TRANSACTION_" . time() . ".csv";
header("Content-Disposition: attachment; filename=\"$myFile\"");
header("Content-Type: application/vnd.ms-excel; charset=UTF-16LE");
$out = fopen("php://output", 'w');
//$csvData = array('Warehouse Id', 'Name','Item No','Minimun Stock','Current Stock');
$csvData = array('No','DATE','USER', 'TRADE TYPE','AMOUNT');
$o = fputcsv($out, $csvData, ',', '"');
$count = 1;;
foreach($preview_result as $key => $value)
{
$csvData = array();
$csvData = array($count,date('Y/m/d',strtotime($value['date'])),$value['current_admin_name'],($value['trade_type'])?'WithDraw' : 'Deposit',$value['amount']);
$o = fputcsv($out, $csvData, ',', '"');
$count++;
}
fclose($fh);
// DOWNLOAD CSV
echo $out;
die;
}
I need to download csv in mobile version as like in desktop version
What i done wrong on this ?
I use the below code to disable the ajax in jquery mobile framework,
<script type="text/javascript">
// do not handle links via ajax by default
$(document).bind("mobileinit", function () { $.mobile.ajaxEnabled = false; });
</script>
This will disable the ajax in page and make redirecting to clicked page.

Categories