What is the benefit of "echo" chunks, while ouputing a file? - php

What is benefit and difference between the following:
Statement 1:
header("Content-type: image/jpeg");
header('Expires: ' . date('r',time() + 864000));
header("Pragma: public");
header("Cache-Control: public");
header("Content-Length: " . strlen($contents));
$splitString = str_split($contents, 1024);
foreach($splitString as $chunk)
echo $chunk;
Statement 2:
header("Content-type: image/jpeg");
header('Expires: ' . date('r',time() + 864000));
header("Pragma: public");
header("Cache-Control: public");
header("Content-Length: " . strlen($contents));
echo $contents;

Due to the way TCP/IP packets are buffered, using echo to send large strings to the client may cause a severe performance hit. Sometimes it can add as much as an entire second to the processing time of the script. This even happens when output buffering is used.
If you need to echo a large string, break it into smaller chunks first and then echo each chunk. So, using method 1 to split the string OR using substr to split it and sending to client performs faster for large files than method 2.

Statement 1 adds unnecessary overhead and obfuscation.

in the first statement content divide into 1024 bytes chunks and one chunk have 1024 bytes content and in the second statement detemine the length of whole content ant then echo this but in first divide in chunk and then echo with help of for each one by one.

If the $contents is very large, you can echo it in chunks to stop the whole thing being echo'd at once.

Related

XAMPP, PHP: echo adds two spaces to blob read from database

I read blob (word 2010 document) from mysql database and store it in $data variable. When I simply store that data directly in PHP like so:
file_put_contents('c:\\temp\\dump.docx', $data);
I can open dump.docx in Word (size matches original file). If I attempt to send $data like this:
ob_start();
header('Content-disposition: attachment; filename=' . $name);
header('Content-type: ' . $type);
ob_clean();
echo $data;
ob_end_flush();
exit;
The stored file is two bytes longer. There are two spaces in front:
To check if I somehow do not output those spaces, I called ob_get_contents() just before echo and dumped content to a file. File has zero bytes.
So it looks like echo is producing those two bytes.
Here's post that helped me:
https://drupal.stackexchange.com/questions/163628/extra-space-at-beginning-of-downloaded-image/163644
ob_start was already called ealier. I needed to call only ob_clean() before sending content.

Can't encode CSV file in UTF8 with PHP

I've been trying, for some time now, to export a properly encoded and formated CSV file with PHP. But it's just not working. I've tried every tip in every CSV/PHP related thread on SOF, I've checked that the data in my database is UTF-8, it is. I've tried stuff like utf8_encode() on the whole CSV-line, I've checked that the actual PHP file is encoded in UTF-8, but still no success. When I run the file on http://csvlint.io/ I just get:
Your CSV appears to be encoded in ASCII-8BIT. We recommend you use UTF-8.
But I can't find a trace of any other encoding than UTF-8 anywhere in my code..
Basically this is my code:
First, I put all my CSV-rows in an array, then do this:
if (count($array) == 0)
{
return NULL;
}
ob_start();
$df = fopen("php://output", 'w');
$csv = utf8_encode("header1|header2|header3|header4|header5|header6|header7\r\n");
foreach($array as $line) {
$csv .= $line . "\r\n";
}
setlocale(LC_ALL, 'sv_SE', "swedish");
fwrite($df, "\xEF\xBB\xBF".$csv);
fclose($df);
return ob_get_clean();
And these are the headers sent:
$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");
header("Content-Encoding: UTF-8");
header("Content-Type: text/csv; charset=UTF-8");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Disposition: attachment;filename={$filename}");
header("Content-Transfer-Encoding: binary");
Any ideas?
The issue is the byte-order mark you're prepending to the output in this line:
fwrite($df, "\xEF\xBB\xBF".$csv);
If you change this to simply
fwrite($df, $csv);
You should find the resulting file validates just fine (or at least, the validator doesn't complain about its encoding).
Arguably this is a problem with the validator, since as the Wikipedia article notes,
The Unicode Standard permits the BOM in UTF-8, but does not require or recommend its use.
I don't recommend you use it either, as most software seems not to recognize byte-order marks. But if you must or you simply prefer to, you can safely ignore the warning from CSVLint.
Since that is apparently not the issue, the next thing I'd look at is whether or not the data is being retrieved from the database in UTF-8. (I'll take your word you've already checked carefully to make sure the data is being stored in UTF-8.) If you're using MySQL, this will depend on the configuration of the database server and any options you may be sending the database after connection.
The PHP manual has a section on character sets and MySQL, and there is also this helpful article about using PHP and MySQL together with UTF-8 data. If you're using a different database system, it likely has equivalent configuration options that should be checked.
The only other suggestions I can make are that you
Move the call to setlocale higher in the script, before string concatenation begins in the foreach loop. (I don't think this setting affects simple concatenation, but I'm not certain.)
Remove the Content-Encoding header from your output, as it is invalid the way it is currently being used.
Try to use this code:
$filename = 'csv/'.date('Y-m-d_H:i:s').'.csv';
$fp = fopen($filename, 'w');
foreach ($csvData as $fields) {
fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF));
fputcsv($fp, $fields, $delimiter = ';');
}
fclose($fp);

Encoding accents csv - PHP

I've been looking through all the answers here and I haven't found the solution.
Here's what I got:
MySQL :
Database & Table encoding => utf8_unicode_ci
I'm trying to convert a an array (containing rows from a query) to CSV
however when i open the csv I get this
Prénom
instead of
Prénom
here's my code
$allQueryRows = array();
while($row_query = $stmt_select->fetch(PDO::FETCH_ASSOC)){
$row_query = array_map("utf8_encode", $row_query);
array_push($allQueryRows, $row_query);
}
download_send_headers("csv" . date("Y-m-d") . ".csv");
echo array2csv($allQueryRows);
die();
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");
}
You should send a SET NAMES utf8; MySQL query before your SELECT query instead of array_mapping your data after.
Then in HTTP headers, send
Content-Type: text/csv; charset=utf-8;
AT first blush it looks like a utf-16 / utf-8 issue. Here's how to start to diagnose it:
First, when you say, "however when i open the csv I get this" what do you mean by "open", i.e. open with what?
I would suggest looking at the file in hex to see exactly what is in the file. It could be that what you are opening it in (your editor or whatever) is what is causing the display coming into your eye ball to be wrong OR it could be that the underlying data that your opening program is seeing is wrong. I think you need to sort out that question first.
(Tip: In general this is the old process of divide and conquer: find a way to test somewhere in the middle of your problem to see which half of your system is causing the problem. The quickest results come from picking test points about half way in the middle of the complexity, not near an edge of the problem, i.e. a Boolean search for the bug. It might not find the problem in the first iteration, but it will help narrow it down.)
Also perhaps you need to tell SQL which to use, e.g. $connection->set_charset("utf8");
Or perhaps what you are seeing is actually being displayed differently from what you think it is because of a utf8/utf16 display level mixup. I generally set stay with utf8 and so set Content-Type: text/plain; charset=UTF-8; (Also if you are viewing this file via your editor make sure it's set to the correct character space.)

Apply text wrap to CSV cell containing long string

i am using php code to export data to CSV file.Everything is working fine as required.but problem is that when there comes long text in a cell.I want to wrap text so that i can increase cell size to handle long text.Below is my code.
header("Content-Type: application/force-download\n");
header("Cache-Control: cache, must-revalidate");
header("Pragma: public");
header("Content-Disposition: attachment; filename=store_earning_report.csv");
echo "Notes \n";
echo $Notes;
echo "\n";
exit;
I have searched but didn't find any solution.Is there any way to handle this problem.
Thank you.
Make sure you are including a comma "," after each field, and "\r\n" to trigger a new line in the .csv file that is created.
A .csv is just a text file with commas used to separate the field values - So there is no way you can control the cell sizes that will appear when the file is first opened in Excel.

Direcly outputting to a CSV from an array without saving to a CSV file

I am looking for a simple way to take an array, turn it into a CSV and have a user be able to download the CSV, all without saving the CSV to the server.
$array = [
['name', 'email'],
['Doe, John', 'johndoe#foo'],
['Jane Doe', 'janedoe#foo'],
['Ron "SuperFly" O\'Neal', 'supafly#foo'],
];
header("Content-type: application/csv");
header("Content-Disposition: attachment; filename=test.csv");
$fp = fopen('php://output', 'w'); // or use php://stdout
foreach ($array as $row) {
fputcsv($fp, $row);
}
see fputcsv()
The benefit of using fputcsv is it will handle escaping and enclosing of fields for you, ensuring valid, rugged output that will never break even if your columns values happen to contain csv control characters such as commas, quotes, newline characters etc... If you don't take care to handle control characters robustly, and instead take a common shortcut such as using implode(',', $row) your (low quality) code will produce broken csv output.
Unfortunately, fputcsv cant output to a string, only to a stream, so I write to php's output stream via php://output which is equivalent to echo'ing it out. Note that just like output via echo, it will go through php's output buffer mechanism, and that includes any possible output handlers, such as ob_gzhandler or customer output buffer callback functions that may modify the output. If you wish to bypass the output buffer, you can instead write directly to php://stdout.
You could also write to php://memory, php://temp, or a file if you need to access the csv file contents as a string before outputting it.
$fp = fopen('php://memory', 'w+'); // or php://temp
foreach ($array as $row) {
fputcsv($fp, $row);
}
// Read it back into a string, if desired.
// Rewind the stream to start.
fseek($fp, 0);
// Read the entire stream back to string.
$csvContents = stream_get_contents($fp);
info on stream wrappers http://www.php.net/manual/en/wrappers.php.php
header("Content-type: application/csv");
header("Content-Disposition: attachment; filename=test.csv");
header("Pragma: no-cache");
header("Expires: 0");
echo "name,city,street\n";
Sometimes it's better to use
header("Content-Type: application/octet-stream");
for IE...

Categories