Exporting CSV | Weird Behaviour - php

So, I think that I have asked this question and others related to it for more than twice. But I cannot figure out what's happening.
I'm using this PHP function to export some data from a table to a CSV table. I am testing on local host ( XAAMP ):
// Export CSV By User Level
public function CSVData($path, $level) {
switch ($level) {
case 0:
$filename = $path."/standard_members_".date('Y')."_".date('m')."_".date('d').".csv";
break;
case 1:
$filename = $path."/special_members_".date('Y')."_".date('m')."_".date('d').".csv";
break;
case 2:
$filename = $path."/admin_members_".date('Y')."_".date('m')."_".date('d').".csv";
break;
default:
break;
}
$sql = "SELECT user_name, user_username, user_email, user_register_date INTO OUTFILE '$filename' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n' FROM users WHERE user_level = '$level'";
if ($stmt = $this->connect->prepare($sql)) {
if(!($stmt->execute())){
print_r($stmt->error);
}
$stmt->close();
} else {
$error = true;
$message['error'] = true;
$message['message'] = CANNOT_PREPARE_DATABASE_CONNECTION_MESSAGE;
return json_encode($message);
}
if(!is_file($filename)) {
die('file not found');
} else {
header('Content-Type: application/csv');
header("Content-Disposition: attachment; filename=$filename");
header('Expires: 0');
header('Pragma: no-cache');
readfile($filename);
}
}
And I have a form that performs the action, I mean it submits the action to the PHP file which processes the above function, and this is the action code:
<?php
include '../assets/class/login/loginsys.php';
$extension = new extension;
if ($_GET['action'] == 0) {
$extension->CSVData('exports', 0);
} elseif ($_GET['action'] == 1) {
$extension->CSVData('exports', 1);
} elseif ($_GET['action'] == 2) {
$extension->CSVData('exports', 2);
}
exit();
?>
What happens is the following:
-when I click the submit button on the form I have it sends either the value 0, 1 or 2 to the action code;
-the action code gets the value and processes accordingly;
-but if I haven't created the folder 'exports' on localhost if would have given me this error:
Can't create/write to file 'C:xampphtdocsloginadminexports\standard_members_2012_01_28.csv' (Errcode: 2);
-and I have created it so it can work;
-now that the folder it's there the CSV file is created and the data is dumped into the CSV table;
-but the file that opens in my browser ( the attachment that downloads like you'd click on a file from some website and downloaded it ) it's empty even though the file exported in 'exports' has the data in it;
-and another thing is that when the file already exists in the folder 'exports' the table that is created is tells me:
File 'exports/admin_members_2012_01_29.csv' already exists
So my question is why is what I described happening ? And is there a way to make the MySql query overwrite the previous CSV file ?

You don't need that temporary file. How about this:
define('CSV_SEPARATOR', ',');
// CSV download headers
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="my.csv"');
// A file handle to PHP output stream
$fp = fopen('php://output', 'w');
// UTF-8 BOM
echo "\xEF\xBB\xBF";
// Get data from database
$data = ...;
// List of columns
$columns = array(
'User name',
'First name',
'Last name'
);
foreach ($data as $key => $row) {
// Write columns
if ($key == 0) {
fputcsv($fp, $columns, CSV_SEPARATOR);
}
// Write data
fputcsv($fp, $row, CSV_SEPARATOR);
}
fclose($fp);
Beautiful, isn't it?

Related

PHP print to txt file from database and download the file, lines are messed up

I have 2 options, one to download all entries of a database; The second option to download only selected values (in a table, passed as array); both options download the entries in a file .txt one per line.
But, sometimes and only on certain lines, the result is messed up, and no new line is created.
Is there any idea on what could cause this?
That's the code for download:
function DownloadAll() {
//DOWNLOAD ALL DATAS
$sql = $conn->prepare($query);
$sql->bind_param('i', $userid);
$sql->execute();
$res = $sql->get_result();
while ($ext = $res->fetch_assoc()) {
$file = fopen('/home/tmp/'.$varr.'_export.txt', "a");
if( strpos(file_get_contents('/home/tmp/'.$varr.'_export.txt'),$ext['account_name']) !== true)
{
$content = $ext['id'];
$content .= ":";
$content .= $ext['name'];
$content .= ":";
$content .= $ext['code'];
fwrite($file, $content);
fclose($file);
} else { fclose($file); }
}
}
function DownloadArray($data) {
//DOWNLOAD FROM SELECTED ROWS ARRAY
foreach($data as $do)
{
$sql = $conn->prepare($query);
$sql->bind_param('ii', $userid, $do);
$sql->execute();
$sql->bind_result($id, $name, $code);
$sql->fetch();
$sql->close();
$file = fopen('/home/tmp/'.$varr.'_export.txt', "a");
if( strpos(file_get_contents('/home/tmp/'.$varr.'_export.txt'),$name) !== true) {
$content = $id;
$content .= ":";
$content .= $name;
$content .= ":";
$content .= $code;
fwrite($file, $content);
fclose($file);
} else { fclose($file); }
}
}
function export() {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($varr.'_export.txt'));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize('/home/tmp/'.$varr.'_export.txt'));
readfile('/home/tmp/'.$varr.'_export.txt');
sleep(1);
unlink('/home/tmp/'.$varr.'_export.txt');
die();
}
And that is an example output (found in the .txt file) with the problem
1:vanne:tsvC2:rika:rgPrp3nierde:9K9g4:Caize:vW5g
5:lina:CZPr6:niki:phv47:hery:sh2u
8:shave:4xRj
9:riuster:S74W
10:bunn:vfH9
11:kei:t8vT
12:phas:R3kK
13:nelyn:Bw14:nedah:JtKu
15:rosi:Tfz4
16:seaur:mDY8
17:andrey:QSAA
18:taled:Tba519:evang:yedM20:taver:qs6n
As you see, some lines are all messed up, it doesn't create a new line and it just put the text right after the last one.
It doesn't happen randomly, only to specific lines with specific content or something, because if I download the same export multiple times, it won't be randomly messed up, all lines are messed up identically on all files.
EDIT
The problem was in the fwrite option not adding the new lines.
fwrite($file, $content.PHP_EOL);
Fixed it.
fwrite() is a binary-safe file write. It doesn't add a new line, and neither does your code.
The newlines you see must be coming from the data in your database.
Add a newline to each line:
fwrite($file, $content.PHP_EOL);
And ensure that your data doesn't have them.

Display success message after downloading CSV

I have developed a small Wordpress Plugin to download as a CSV file users' data from the database.
Everything works fine, although I am having issues figuring out how to display a success message.
Below the code of the method that takes care of the data export:
/*
* Export CSV File
*/
public function export_CSV()
{
if ($this->users) {
$fileName = "Export_users_" . date("Y-m-d_H:i:s", time());
$fileName = $fileName.".csv";
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Description: File Transfer');
header('Content-Encoding: UTF-8');
header("Content-type: application/csv;charset=UTF-8");
header("Content-Disposition: attachment; filename={$fileName}");
header("Expires: 0");
header("Pragma: public");
echo "\xEF\xBB\xBF"; // UTF-8 BOM
$fh = #fopen( 'php://output', 'w' );
$headerDisplayed = false;
foreach ($this->users as $user) {
if($user) {
$user_meta = get_user_meta($user->data->ID);
//Push user meta into $user array
foreach($user_meta as $key => $var)
{
$user->data->$key = trim($var[0]);
}
//convert object in array;
$usersArray = (array)$user->data;
// Add a header row if it hasn't been added yet
if ( $headerDisplayed == false ) {
// Use the keys from $directory as the titles
fputcsv($fh, array_keys($usersArray));
$headerDisplayed = true;
}
// Put the data into the stream
fputcsv($fh,$usersArray);
}//end if
}//end foreach
// Close the file
fclose($fh);
// Users exported successfully
$this->response = "Users exported successfully!";
// Make sure nothing else is sent
exit;
} else {
// No results found
$this->response = "Sorry, no results found!";
}
}
I have to use exit otherwise the CSV file will contain all the html of the page. Is there a way to run a javascript file to update the message in the front end?
Thanks in advance!
First of all:
Put the download details in a separate page. You can only send the headers once, that's one of the reasons why you can't print html after sending the csv header.
Secondly you could then create an if/else statement for if the function has succeeded. and if it did you could add some JS code like the example below:
if($download = true) {
echo "<script> alert('Download succesfull!'); </script>";
}
else {
echo "<script> alert('Download failed!'); </script>";
}
Tho i'm not sure if it could work like above since it's an server event. Another possible solution would be by creating a ServerEventCheck.
I came up with a solution by passing the response message as a variable in the header.
$this->response = "Users downloaded Successfully!";
header("Location:?page=export-users&response=".$this->response);
and then retrieve it like so:
<?php if(isset($_GET['response'])){ ?>
<div class="updated notice">
<p><?php echo __( $_GET['response']);?></p>
</div><!--error notice-->
<?php } ?>

Saving CSV file instead of downloading

So I have this code to generate a CSV file from mysql database. But however, it downloads the code instead of saving it to the directory for further use (need to send that file via phpmailer).
What changes should I do to make it save the file to the directory.
$array = array();
if(file_exists('records_monthly.csv'))
{
unlink('records_monthly.csv');
}
# Headers
$array[] = array("Serial Number","Donation Type", "Amount", "Status", "Date", "Orderref", "DIN");
$serial=1;
try
{
$s = $conn->query("SELECT c.firstname AS firstname, c.lastname as lastname, c.address AS address, c.city AS city, c.postalnumber AS postalnumber, c.email AS cemail, d.donation_type as donation_type, d.donation_amount as donation_amount, d.orderref as orderref, d.status as status, d.donation_on AS donation_on, d.din as din from customers c, donations d where c.email = d.donator");
}
catch(PDOException $e)
{
echo $e->getMessage();
}
while($donations = $s->fetch(PDO::FETCH_OBJ))
{
if($donations->status == 0)
{
$array[] = array($serial++,$donations->donation_type,$donations->donation_amount,"Failed",$donations->donation_on,$donations->orderref,$donations->din);
}
else
{
$array[] = array($serial++,$donations->donation_type,$donations->donation_amount,"Success",$donations->donation_on,$donations->orderref,$donations->din);
}
}
array_to_csv_download($array,"records_monthly.csv",",");
function array_to_csv_download($array, $filename = "export.csv", $delimiter=";") {
// open raw memory as file so no temp files needed, you might run out of memory though
$f = fopen('php://memory', 'w');
// loop over the input array
foreach ($array as $line) {
// generate csv lines from the inner arrays
fputcsv($f, $line, $delimiter);
}
// rewind the "file" with the csv lines
fseek($f, 0);
// tell the browser it's going to be a csv file
header('Content-Type: application/csv');
// tell the browser we want to save it instead of displaying it
header('Content-Disposition: attachement; filename="'.$filename.'";');
// make php send the generated csv lines to the browser
fpassthru($f);
}
Write directly to the named file rather than to php://memory and don't send headers and output to the browser
function array_to_csv_without_download($array, $filename = "export.csv", $delimiter=";") {
$f = fopen($filename, 'w');
// loop over the input array
foreach ($array as $line) {
// generate csv lines from the inner arrays
fputcsv($f, $line, $delimiter);
}
fclose($f);
}

Using PHP to generate CSV file from MySQLi is saving but not asking to download

So my following code generated a CSV based on specified tables and generates file and saves to downloads/filename.csv however its not asking the user to download once its generated. Any ideas why?
Here is the code:
PHP
header("Content-type: text/csv");
// Connect to the database
$mysqli = new mysqli(DATABASE_HOST, DATABASE_USER, DATABASE_PASS, DATABASE_NAME);
// output any connection error
if ($mysqli->connect_error) {
die('Error : ('.$mysqli->connect_errno .') '. $mysqli->connect_error);
}
$tables = array('invoices', 'customers', 'invoice_items'); // array of tables need to export
$file_name = 'invoice-export-'.date('d-m-Y').'.csv'; // file name
$file_path = 'downloads/'.$file_name; // file path
$file = fopen($file_path, "w"); // open a file in write mode
chmod($file_path, 0777); // set the file permission
// loop for tables
foreach($tables as $table) {
$table_column = array();
$query_table_columns = "SHOW COLUMNS FROM $table";
// fetch table field names
if ($result_column = mysqli_query($mysqli, $query_table_columns)) {
while ($column = $result_column->fetch_row()) {
$table_column[] = $column[0];
}
}
// Format array as CSV and write to file pointer
fputcsv($file, $table_column, ",", '"');
$query_table_columns_data = "SELECT * FROM $table";
if ($result_column_data = mysqli_query($mysqli, $query_table_columns_data)) {
// fetch table fields data
while ($column_data = $result_column_data->fetch_row()) {
$table_column_data = array();
foreach($column_data as $data) {
$table_column_data[] = $data;
}
// Format array as CSV and write to file pointer
fputcsv($file, $table_column_data, ",", '"');
}
}
}
// close file pointer
fclose($file);
// ask either save or open
header("Pragma: public");
header("Expires: 0");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename='{$file_name}';" );
header("Content-Transfer-Encoding: binary");
// open a saved file to read data
$fhandle = fopen($file_path, 'r');
fpassthru($fhandle);
fclose($fhandle);
$mysqli->close();
die;
This should allow you to select the field you want to write to the CSV file, and their order.
// loop over the rows, outputting them
while($row = $results->fetch_assoc()) {
$data = [
$row["myfirstfield]",
$row["mysecondfield"],
....
];
fputcsv($output, $data);
}

Export CSV file using PHP create corrupt file when try to open in MS Excel

I have the following script that will export data from my database using php to csv file. Everything works fine, except when I try to open the file in excel, I get "file is corrupt". When I run this code it shows the error - "The file is corrupted and can not be opened." Thanks in advance!
<?php
// Connection
include_once('conn.php');
$sql = "select * from info";
$qur = mysql_query($sql);
// Enable to download this file
$filename = "sampledata.csv";
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Content-Type: text/csv");
$display = fopen("php://output", 'w');
$flag = false;
while($row = mysql_fetch_assoc($qur)) {
if(!$flag) {
// display field/column names as first row
fputcsv($display, array_keys($row), ",", '"');
$flag = true;
}
fputcsv($display, array_values($row), ",", '"');
}
fclose($display);
exit;
?>
I found the answer of my own question. Just need to add the ob_clean() line before we call the headers to download csv file to clear the output buffer. This will solve the error of not opening csv file in excel.

Categories