This piece of code NEARLY solves my issue saving a query to .csv via php – however I can't understand why when it prompts me to save as export.csv, that file opens blank, while file.csv saves perfectly at the same time with the data I need. I'm sure it's something obvious going on with the headers. Can anyone help?
***EDIT below suggestions got me to this state and it no longer saves a file and prompts for a blank one but now the correct file shows with HTML crap in it. There is no other HTML in this script -- I've tried suggestions from many other posts. How can I solve?
$query = "SELECT * from Table";
$result = mysqli_query($connS, $query);
$headers = $result->fetch_fields();
foreach($headers as $header) {
$head[] = $header->name;
}
$fp = fopen('php://output', 'w');
header('Content-Disposition: attachment; filename="export.csv"');
header('Content-Description: File Transfer');
header('Content-Type: text/csv');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
fputcsv($fp, array_values($head));
while ($row = $result->fetch_array(MYSQLI_NUM)) {
fputcsv($fp, array_values($row));
}
$fp = fopen('file.csv', 'w');
readfile($fp);
fclose($fp);
end();
You're currently only saving the contents to file, not sending it to the user's browser. Perhaps try adding a call to readfile before your die statement.
So your last rows would look like this:
while ($row = $result->fetch_array(MYSQLI_NUM)) {
fputcsv($fp, array_values($row));
}
readfile($fp);
die;
}
Readfile reads a file and writes it to the output buffer. You can find more information on the readfile function here.
Note that the way you're currently doing it(saving to file before the user can download), if two users hit that page simultaneously, they would likely stomp on each others' toes(and create some odd intermix of the two created CSVs). You may want to try simply outputting the CSV contents directly to the user's browser, using the code in the accepted answer there. If you do that after your headers, it should result in the browser treating it like a file to be downloaded.
Related
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));
}
I'm trying to view files (i.e: excel sheets/pdf/images) on browser that are stored in database.
I already wrote a code for downloading the files from the database and it is working but I want to display it in the browser.
Here is the code:
<?php require_once('Connections/databasestudents.php'); ?>
<?php
$id = $_GET['id']; // ID of entry you wish to view. To use this enter "view.php?id=x" where x is the entry you wish to view.
$query = "SELECT fileContent, filetype FROM file where id = $id"; //Find the file, pull the filecontents and the filetype
$result = MYSQL_QUERY($query); // run the query
if($row=mysql_fetch_row($result)) // pull the first row of the result into an array(there will only be one)
{
$data = $row[0]; // First bit is the data
$type = $row[1]; // second is the filename
Header( "Content-type: $type"); // Send the header of the approptiate file type, if it's' a image you want it to show as one :)
print $data; // Send the data.
}
else // the id was invalid
{
echo "invalid id";
}
?>
What happens is that view.php is downloaded and nothing is viewed.
Any suggestions?
According to your code, $row[1] is "the filename". The Content type header should contain the content type instead, i.e. the file mime type, for example:
header('Content-type: application/pdf');
If you want to add a filename:
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename='.$row[1]);
print $data;
Be sure $data is the content of the file, something you can take from readfile() for example.
Read more on the manual: http://php.net/manual/en/function.readfile.php
Keep in mind that while PDF and images are easily viewable by a browser, I think Excel needs some ad hoc plugin for that.
A more complete example right out of the manual, to get you a more thorough idea (not all those headers are necessary, and you should change others accordingly to your code):
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
download.php:
<?php require_once('Connections/connection_psfriend.php'); ?>
<?php
$idreceived = addslashes($_REQUEST['sendid']);
$filepathquery = "SELECT bd_brushfilepath FROM tbl_brushdescription WHERE bd_brushid = $idreceived";
$Recordset = mysql_query($filepathquery,$connection_psfriend) or die(mysql_error());
$filepath = mysql_fetch_assoc($Recordset);
$receivedfilerequest = $filepath['bd_brushfilepath'];
$file_path = $_SERVER['DOCUMENT_ROOT'].'/'.'ps-friend'.'/' . $receivedfilerequest;
$updatedownlaodquery = "UPDATE tbl_brushdescription SET bd_brushdownloads = bd_brushdownloads + 1 WHERE bd_brushid = $idreceived";
$Recordset = mysql_query($updatedownlaodquery,$connection_psfriend) or die(mysql_error());
if(file_exists( $file_path)){
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file_path));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file_path));
ob_clean();
flush();
readfile($file_path);
exit;
}
My problem:
The code works fine with google Chrome for all 143 entries made in the database. It works fine fine with Firefox too except for 5 out of those 143.
for firefox it shows:( for those 5 entries):
In the database, I am using filepaths to store the files. All the files are either in zip format or rar format. Those files are not downloaded in rar/zip format. With google chrome, there is no problem at all. Is there something wrong with the script?
First of all, please stop using MYSQL_ function, see why-shouldnt-i-use-mysql-functions-in-php.
Secondly you are using $idreceived = addslashes($_REQUEST['sendid']); as a nice way to prevent SQL injection. Unfortunately you are calling the query like WHERE bd_brushid = $idreceived, so without quotes.
In other words, you are still vulnerable, as long as I dont use any quotes. Consider me sending sendid=1 OR 1=1. All your rows would be updated in the second query.
Change it to WHERE bd_brushid = '$idreceived', or even better: check how-to-prevent-sql-injection-in-php
Now to your problem. I think you should change one line to the following to include quotes
header('Content-Disposition: attachment; filename="'.basename($file_path).'"');
If it still does not work, send the right content type in the header.
<?php
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$type = finfo_file($finfo, $file_path);
header('Content-Type: '.$type)
?>
The following has always worked for me
$file=$_POST['file'];
header( "Content-Disposition: attachment; filename=$file" );
readfile("$_POST[file]");
without any of the content type headers.However my file extension is .zip.Your file does not seem to have an extension.
First time I've had to do this, but I need to save the results of a query to CSV and create a file on the server, prior to it being passed via SFTP to a remote server. I can successfully create the CSV which is downloaded in the browser, per the code below, but cant seem to get it to save a file on the server. I think I may need file_put_contents? If anyone can suggest a method, or indeed a better approach to sending the outputs via sftp, its much apopreciated.
$result = mysql_query("SELECT * FROM `policy_details` WHERE `policyNumber` = '848938'");
if (!$result) die('Couldn\'t fetch records');
$num_fields = mysql_num_fields($result);
$headers = array();
for ($i = 0; $i < $num_fields; $i++)
{
$headers[] = mysql_field_name($result , $i);
}
$fp = fopen('php://output', 'w');
if ($fp && $result)
{
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="export.csv"');
header('Pragma: no-cache');
header('Expires: 0');
fputcsv($fp, $headers);
while ($row = mysql_fetch_row($result))
{
fputcsv($fp, array_values($row));
}
//die;
}
Immediate thought would be that you don't need to send headers when saving a file. If you are just saving a regular csv file to the local server (and doing the FTP thing separately), I also don't see the benefit of using the 'php://' scheme. I'd use something like
$fp = fopen('/path/to/new/file.csv', 'w+');
if ($fp && $result)
{
while ($row = mysql_fetch_row($result))
{
fputcsv($fp, array_values($row));
}
}
On the other hand, you could save the file directly over FTP, and avoid a 2 stage process.
What I would do is change your php://output file pointer to the file on the server that you want to save to. Move your headers below your while loop, that way you're writing each line from the db to the file. Once you exit your while loop, read the file that you just wrote as one big variable and print echo it with your headers.
$fp=fopen('/path/to/file/on/server/','w');
while ($row = mysql_fetch_row($result))
{
fputcsv($fp, array_values($row));
}
fclose($fp);
$csvFile = fopen('/path/to/file/on/server','r');
$csvData = fread($csvFile,filesize('/path/to/file/on/server');
fclose($csvFile);
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="export.csv"');
header('Pragma: no-cache');
header('Expires: 0');
echo $csvData;
Obviously this isn't the whole script, but these are the pieces I would change to accomplish what you want to do.
Using PHP, I'm exporting results of a query to CSV. My problem comes when the data contains accent; they are not exported correctly and I lose them all in the generated file.
I used the utf8_decode() function to manually convert the headers and it worked perfectly, but I don't know how to use it for the results array.
Anyone can help me out please!?
result = db_query($sql);
if (!$result) die('Couldn\'t fetch records');
$fp = fopen('php://output', 'w');
if ($fp && $result) {
header("Content-type: application/vnd.ms-excel; charset=UTF-8");
header('Content-Disposition: attachment; filename="adp_enigmes_data.csv"');
header('Pragma: no-cache');
header('Expires: 0');
fputcsv($fp, $headerTitles);
while ($row = $result->fetch_array(MYSQLI_NUM)) {
// When I use utf8_decode here, I don't get any results, so I have
// no idea where to use it!
fputcsv($fp, utf8_decode(array_values($row)), ',', '"');
}
die;
}
Apply utf8_decode to all elements in result row, so simply array_map:
fputcsv($fp, array_map('utf8_decode',array_values($row)), ',', '"');