This question already has answers here:
Create a CSV File for a user in PHP
(20 answers)
Closed 8 years ago.
I want to create and download a CSV file, in one script.
Up to now I have been avoiding my lack of knowledge by pre-creating the file with a cron job and then
downloading the file via a link.
I have this:
$conn = new mysqli($dbhost, $dbuser, $dbpass, $dbname) or die ('Error connecting to mysql');
$get_members_csv_query = "SELECT * FROM members";
$get_members_csv_result=$conn->query($get_members_csv_query, MYSQLI_STORE_RESULT);
$all_members_array = array();
while ($get_members_csv_array = $get_members_csv_result->fetch_assoc())
{
array_push($all_members_array, $get_members_csv_array);
}
$file = fopen('members.csv', 'w');
fputcsv($file, array('email', 'First Name', 'Surname', 'Gender','DOB','Registered On','Country','paid','Children','Wants Kids','Relationship','Body Type','Height','Smoke','Drink','Phone','Region','Religion','Community'));
foreach ($all_members_array as $row) {
fputcsv($file, $row);
}
exit();
This creates the file successfully, but what do I need to add to the script to make it automatically download the CSV file once created?
I have tried putting this at the start if the script:
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=members.csv");
header("Pragma: no-cache");
header("Expires: 0");
But it downloads the file before the script finishes (or even starts) creating the file.
I have tries putting the headers at the end of the script and using ob_start() at the top - same result.
Thanks
You're never sending the contents of the file to the browser. It should be:
$file = fopen('members.csv', 'w');
fputcsv($file, array('email', 'First Name', 'Surname', 'Gender','DOB','Registered On','Country','paid','Children','Wants Kids','Relationship','Body Type','Height','Smoke','Drink','Phone','Region','Religion','Community'));
foreach ($all_members_array as $row) {
fputcsv($file, $row);
}
fclose($file);
// Download file
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=members.csv");
header("Pragma: no-cache");
header("Expires: 0");
readfile('members.csv');
exit();
Try this:
From the exit() line, modify to:
exit(dnl("members.csv"));//call dnl function
then on the same page write the dnl function:
function dnl($fn){
// Send Header
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");;
header("Content-Disposition: attachment;filename=$fn");
header("Content-Transfer-Encoding: binary ");
}
Remember that the content-type "application/octet-stream" can be changed to match only csv as you have done before.
Related
I have a 3rd party source from where I am getting "csv" file. I wrote it inside a quote because it says it's a csv file but basically it's not.
So I am taking that main source file then reading and putting the data in a "PROPER" csv file.
The read and write is fine but the problem is when it saves the properly quoted data is writing on the script file itself.For example if the my php file name is "fixcsv.php" then I am getting the downloadable file as "fixcsv.php".
My code
$headings = array('HID');
$handle = fopen("MonC1.csv", "r");
$data = fgetcsv($handle, 0, ";",'"');
$fh = fopen('php://output', 'w');
ob_start();
fputcsv($fh, $headings);
// Loop over the * to export
if (! empty($data)) {
foreach ($data as $item) {
// echo $item;
fputcsv($fh, array($item));
}
}
$string = ob_get_clean();
$filename = 'csv_' . date('Ymd') .'_' . date('His');
// Output CSV-specific headers
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=\"$filename.csv\";" );
header("Content-Transfer-Encoding: binary");
exit($string);
Any help is highly appreciated. Thanks in advance.
Your Content-Disposition has a semi-colon in the wrong place (per the spec). Should be:
header("Content-Disposition: attachment; filename=\"$filename.csv\" );
We run queries using CodeIgniter against our MySQL database, and display those results on the page. Sometimes, we want to download those as a CSV. The guy before me set up this download button, but it does not function at all. Never has.
This is the existing code for the download:
public function download_csv_search(){
$dbresults = $this->do_search();
$results = $dbresults['results'];
$csv = "First Name, Last Name, MI, Age, Details(RA), Status, Home Phone, Gender, Notes\n";
$filename = "data_export";
//header('Content-Type: text/csv; charset=utf-8');
//header('Content-Disposition: attachment; filename=data.csv');
//$output = fopen('php://output', 'w');
//fputcsv($output, array('First Name', 'Last Name', 'Middle Name', 'DOB', 'Details(RA)', 'Status', 'Home Phone', 'Gender', 'Notes'));
foreach($results as $result) {
$birthday_timestamp = strtotime($result->VoterDOB);
$age = date('md', $birthday_timestamp) > date('md') ? date('Y') - date('Y', $birthday_timestamp) - 1 : date('Y') - date('Y', $birthday_timestamp);
$csv .= $result->VoterFN.",".$result->VoterLN.",".$result->VoterMN.",".$dob.",".$result->Street.",".$result->VoterStatusID.",".$result->phone.",N/A,\n";
// $dataArray = array('VoterFN'=>$result->VoterFN, 'VoterLN'=>$result->$VoterLN, 'VoterMN'=>$result->VoterMN,'VoterDOB'=>$result->date('m/d/Y', strtotime($result->VoterDOB)), 'Street'=>$result->Street, "VoterStatusID"=>$result->VoterStatusID,'phone'=>$result->phone);
//fputcsv($output, $dataArray);
}
//OUPUT HEADERS
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=\"$filename.csv\";" );
header("Content-Transfer-Encoding: binary");
//OUTPUT CSV CONTENT
echo($csv);
exit();
}
I'm guessing I need to use the download helper + the force_download function, but I can't seem to get it. Any ideas?
I think you have so many headers, but more important, if the file is a CSV, why sending it as a binary file? Why aren't you using text/csv? I only use the following code, I suppose that Cache-Control is to avoid downloading the same file without hit the server. Anyway, only with:
header('Content-type: text/csv');
header("Content-Disposition: attachment; filename=\"$filename.csv\";" );
should work. After a bit of research in SO, depending on what browser you use you may have problems with your Cache headers:
Creating and downloading CSV with PHP, so research a bit that in order to avoid future issues.
I can't get the browser to prompt for download. The output gets displayed on the screen instead. I've tried so many other threads regarding this topic on this site but to no avail. I could change the fopen("php://output","w") to fopen("export.csv","w") but that will save a copy of the export.csv file on the server, which I don't want. I want the file to be downloaded on the client without it being saved on the server. Here's my code:
$sql = mysql_query($_SESSION["export-query"]);
$fields = mysql_num_fields($sql);
$header = array();
for ($i = 0; $i < $fields; $i++) {
$header[] = mysql_field_name($sql, $i);
}
$f = fopen("php://output","w");
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename=export.csv');
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate');
fputcsv($f, $header);
while ($row = mysql_fetch_row($sql)) {
fputcsv($f, $row);
}
fclose($f);
Please help! Much appreciated.
Your code is really close to mine, but I have this
header("Content-type: application/vnd.ms-excel");
for the content type. I think this works because browsers know how to handle text/csv but they don't know how to handle excel. It will prompt to download because it doesn't open this type of file itself.
I also don't have "must-revalidate," but I don't think that makes a difference.
EDIT:
Here are my full headers, which have worked 100% of the time. There are minor differences from yours, so maybe one of them is the reason.
header("Content-type: application/vnd.ms-excel");
header("Content-disposition: attachment; filename=".$filename.".csv");
header("Pragma: no-cache");
header("Expires: 0");
EDIT 2:
Judging from your comment on your answer, you are putting all of this code as an ajax call inside a div. The reason that doesn't work is that you can only set headers on the initial call to a page. Setting headers on an ajax call will be ignored.
Here is how my system handles csv generation. Because I needed specific information that could vary between different csv files, I put the name of the generator file into the "action" of a form and provided a submit button:
<form action="thegeneratorpage.php" method="get"><fieldset>
<p>Download [...] in .csv (Excel) form. You can narrow by [...].</p>
<!-- code here that allows users to narrow down what is in the csv -->
<input type="submit" value="Download" />
</fieldset></form>
If the information doesn't vary, you can just do this:
Download CSV
All the code we have been discussing would be on thegeneratorpage.php.
Rather than using the fputcsv function, I would suggest just echoing the rows of the CSV file like so (note that the headers I use are slightly different from yours):
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={$fileName}");
header("Content-Transfer-Encoding: binary");
while ($row = mysql_fetch_row($sql)) {
// optionally enclose data if necessary
foreach ($row as $k => $v) {
if (strpos($v, ',') !== false) {
$row[$k] = '"' . $v . '"';
}
}
echo implode(',', array_values($row));
}
So what I am trying to do is take 2 strings and create 2 files. Then create a zip out of these files and let a user download them.
Here is what I have:
$string1 = 'Some data some data some data';
$string2 = 'Some data some data some data';
$zip = new ZipArchive();
$filename = "test.zip";
if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
exit("cannot open <$filename>\n");
}
$zip->addFromString("string1.txt", $string1);
$zip->addFromString("string2.txt", $string2);
$zip->close();
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize('test.zip'));
So far no luck. Any help is appreciated.
You missed the most important part - output the file! :)
Add:
readfile('test.zip');
to the end of the php file.
Also the calculation of the HTTP content-length header is wrong:
header("Content-Length: ".filesize($zip));
This will give you always 0 ( or false) as filesize expects a filename as its argument.
Change the line to:
header("Content-Length: ".filesize('test.zip'));
After doing both of this the zip will successfully download and contains the two files. For completenes, here comes the full working example:
$string1 = 'Some data some data some data';
$string2 = 'Some data some data some data';
$zip = new ZipArchive();
$filename = "test.zip";
if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
exit("cannot open <$filename>\n");
}
$zip->addFromString("string1.txt", $string1);
$zip->addFromString("string2.txt", $string1);
$zip->close();
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
// make sure the file size isn't cached
clearstatcache();
header("Content-Length: ".filesize('test.zip'));
// output the file
readfile('test.zip');
You have a PHP error (you probably dont have error reporting turned on or high enough error level).
the filesize() function takes a string not an object. filesize($filename) will work.
to turn on error reporting do:
error_reporting(E_ALL|E_STRICT);
ini_set('display_errors', 1);
alternatively do this in php.ini
After all those header() calls, I think you want:
readfile($filename);
I have done a script in PHP to export a MySQL table into a CVS file when clicking a button. It's working fine but now I need to export 1 table per CSV file, all this in a ZIP (or RAR) file when clicking a button.
Basically it should be:
Export table:
mytable.csv
Export all tables:
export.ZIP containing:
--mytable1.csv
--mytable2.csv
--mytable3.csv
I could loop the export function for each table but that will not put everyting into the ZIP:
/* EXPORT ALL TABLES */
if (isset($_POST['export_all_tables'])){
$Qlist_table = $pdo->prepare('SHOW TABLES');
$Qlist_table->execute(array(''));
foreach ($Qlist_table as $Alist_table)
export_table($Alist_table[0]);
}
function export_table($table_to_export){
$Qselect = $pdo->prepare('SELECT * FROM '.$table_to_export.'');
$Qselect->execute(array(''));
$results = $Qselect->fetchAll(PDO::FETCH_ASSOC);
...
...
...
header("Content-disposition: attachment; filename=".$fileName);
header("Content-Type: application/force-download");
header("Content-Transfer-Encoding: application/vnd.ms-excel\n");
header("Pragma: no-cache");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0, public");
header("Expires: 0");
echo $outputCsv;
exit();
}
I don't think you need all the code of the export function, if yes let me know.
Thanks!
Actually I found the solution thanks to addFile(). Here is the code:
if (isset($_POST['export_all_tables'])){
// Name of the file to export
$date = date('Y_m_j_H\hi');
$fileName = 'Export_all_'.$date.'.zip';
// Headers to create the ZIP file
header("Content-disposition: attachment; filename=".$fileName);
header("Content-Type: application/force-download");
header("Content-Transfer-Encoding: application/zip;\n");
header("Pragma: no-cache");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0, public");
header("Expires: 0");
// We create the ZIP file
$zip = new ZipArchive;
$result_zip = $zip->open($fileName, ZipArchive::CREATE); // We open the file
if ($result_zip === TRUE) {
$Qlist_table = $pdo->prepare('SHOW TABLES');
$Qlist_table->execute(array(''));
// For each table
foreach ($Qlist_table as $Alist_table) {
$content = export_table($Alist_table[0]);
$fileName = $Alist_table[0].'_'.$date.'.csv';
$zip->addFile($fileName, $fileName); // We add the CSV file from the server
}
$zip->close();
}
else {
echo 'Failed, code:' . $result_zip;
}
readfile("Export_all.zip"); // To download the ZIP file
exit();
}
and the exporttable function ends like that to write the file on the server:
$fh = fopen($fileName, 'w') or die("Can't open file");
$stringData = $outputCsv; // outputCsv is the content of the table
fwrite($fh, $stringData);
fclose($fh);