yii send csv file charset - php

I work with yii-powered application. My goal is write controller action what exporting some data from mongodb to csv file using Yii 1.1: csvexport and CHttpRequest::sendFile
My code:
public function actionCatalogDataExport( $catalog_id )
{
// prepare all needed variables here
$data = ..., $headers = ..., $filename = ...
Yii::import('ext.csv.ECSVExport');
$csv = new ECSVExport($data);
$output = $csv->setHeaders($headers)->setDelimiter(',')->toCSV();
Yii::app()->getRequest()->sendFile($filename, $output, "text/csv", true);
}
This script works properly, but if I open resulting file via Excel I see something like that:
There are some problems with file encoding... I opened notepad++ and changed encoding to UTF-8 without BOM, now file looks good (language: Ru):
Tested this fixes but no success results:
header('Content-type: text/csv; charset=UTF-8'); // no effect
Yii::app()->getRequest()->sendFile(
$filename,
$output,
"text/csv; charset=UTF-8", // no effect
true
);
How can I achieve this immediately after yii send file action?

Try to add the encode to begin of the csv file like this:
$encode = "\xEF\xBB\xBF"; // UTF-8 BOM
$content = $encode . $csv->toCSV();
//var_dump($content);
Yii::app()->getRequest()->sendFile($filename, $content, "text/csv; charset=UTF-8", false);

By default setup params, Excel for Windows opens csv files using windows-1251 encoding. If I need to make correct data values using this encoding I must use iconv
foreach( $data as $key => &$value ) {
$value = iconv('UTF-8', 'windows-1251', $value);
}
// send file to user...
// ...and it works as I need.

Related

php write csv to file returns blank file

I have some csv data that looks like this:
$data = 'email,score
john#do.com,3
test#test.com,4';
When I try to export this csv to a file like this:
$response = new StreamedResponse();
$response->setCallback(
static function () use ($data): void {
$fp = fopen('php://output', 'wb');
file_put_contents('exportk.csv', $data);
fclose($fp);
}
);
$response->setStatusCode(200);
$response->headers->set('Content-Type', 'text/csv; charset=utf-8');
$response->headers->set('Content-Disposition', 'attachment; filename="export.csv"');
I get an empty file, what am I doing wrong here
From the Symfony docs:
https://symfony.com/doc/current/components/http_foundation.html#request
If you just created the file during this same request, the file may be sent without any content. This may be due to cached file stats that return zero for the size of the file. To fix this issue, call clearstatcache(true, $file) with the path to the binary file.
If that doesn't fix the issue, maybe try something like this:
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
$data = <<<END
email,score
john#do.com,3
test#test.com,4
END;
// Just write the file here to save to the file system if you want...
$response = new Response($data);
$disposition = HeaderUtils::makeDisposition(
HeaderUtils::DISPOSITION_ATTACHMENT,
'export.csv'
);
$response->headers->set('Content-Type', 'text/csv; charset=utf-8');
$response->headers->set('Content-Disposition', $disposition);

Safari (or Apple OS) adding .txt to .csv download

I am using PHP to create a simple .csv from database entries in Laravel, here's a little sample of the code:
$file = fopen(storage_path('file.csv'), 'w');
//$printer = array from database
foreach ($printer as $row) {
fputcsv($file, $row);
}
fclose($file);
//$formname = Object values
$headers = array(
'Content-Type: text/csv',
'Content-Length: ' . filesize(storage_path('file.csv')),
'Content-Disposition: attachment; filename="'.$formname->name.'.csv"'
);
return Response::download(storage_path('file.csv'), $formname->name.'.csv', $headers);
All pretty standard and work's fine for me, but my Client is using OS X 10.9.3 & Safari 7.0.4 and each download is given a .txt extension and I can't find a way to stop it from either server or client side. (so it downloads as file.csv.txt)
I have searched Google but can only find other users with the problem - no solution, the code above is already edited using suggestions found on Stackoverflow:
1. laravel adding .txt on the downloaded file
2. How to use the CSV MIME-type?
Can anyone tell me where this problem arises and how to fix it?
And is there anywhere I can test the download as I only have Windows and the testing websites I use only provide visual renders.
Also: The client has assured me he can download .csv's from other sources without this problem
As I can see Symfony iterates through the headers as key-value pairs:
public function __construct(array $headers = array())
{
foreach ($headers as $key => $values) {
$this->set($key, $values);
}
}
And then sets them this way:
if (true === $replace || !isset($this->headers[$key])) {
$this->headers[$key] = $values;
} else {
$this->headers[$key] = array_merge($this->headers[$key], $values);
}
So if you only have values in the $headers array with numeric indexes, the result might be unexpected. Try to change your code to this:
$headers = array(
'Content-Type' => 'text/csv',
'Content-Length' => filesize(storage_path('file.csv')),
'Content-Disposition' => 'attachment; filename="'.$formname->name.'.csv"'
);

How to generate UTF8 CSV file in php with well rendered quotes?

I want to create a CSV file in php, encoded in UTF-8, all caracters are well rendered (even accents, cause I am french), except quotes, that are rendered as "'".
Can anyone help me?
Here is an example of my code, in Symfony2
$response = new Response();
$response->headers->set('Content-Type', 'text/csv');
$response->headers->set('Content-disposition', 'attachment; filename="'.$name.'.csv"');
$response->setContent(utf8_decode($csv));
$response->setStatusCode(200);
Thank's a lot
Some pointers for you:
utf8_decode produces ISO-8859-1 content. Therefore your output is not UTF-8.
' is the same in UTF-8, ASCII, Windows-1252 and ISO-8859-1
I suspect ' is coming from your CSV source.
If you're building the source, ensure you use UTF-8 chars. Then you don't need to convert. If you're reading from disk, make sure you know the original character set. Don't convert character sets unless you know the original character set.
I am using Symfony 4.4.
These are the 2 solutions that worked for me :
1. First one:
// This import is used to inject a serializer object
use Symfony\Component\Serializer\SerializerInterface;
$content = $this->serializer->serialize($csv, 'csv', [
'csv_delimiter' => ';',
'output_utf8_bom' => true,
]);
$response = new Response($content);
$response->headers->set('Cache-Control', 'no-cache');
$response->headers->set('Content-Type', 'application/force-download');
$response->headers->set('Content-Disposition', $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT, "$name.csv"
));
2. Second one:
headers => contains your headers.
lines => contains your lines.
$response = new StreamedResponse();
$response->headers->set('Cache-Control', 'no-cache');
$response->headers->set('Content-Type', 'application/force-download');
$response->headers->set('Content-Disposition', $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
"$name.csv"
));
$response->setCallback(function () use ($headers, $lines) {
$handle = fopen('php://output', 'w+');
// Mandatory: Use bom + "Content-Type: application/force-download" => to allow to display special characters with excel
fwrite($handle, $bom = chr(hexdec('EF')).chr(hexdec('BB')).chr(hexdec('BF')));
// headers
fputcsv($handle, $headers, ';');
// data
foreach ($lines as $line) {
fputcsv($handle, $line, ';');
}
fclose($handle);
});

Convert "Java source code characters" in JSON string using PHP

I have a JSON string that contains Dal\u00e9. When I use json_decode on the JSON, it is converted to Dalé, however the original string that the JSON is from is Dalé. Why is this not converted properly?
I have found that "\u00E9" is the C/C++/Java source code encoding for é. However, to me this doesn't answer why this is going wrong.
Example of incorrect PHP output:
<?php
$opts = array('http'=>array('ignore_errors' => true));
$context = stream_context_create($opts);
$jsonurl = "http://api.kivaws.org/v1/loans/552804.json";
$json = file_get_contents($jsonurl, false, $context);
$json_output = array(json_decode($json));
$json_error = $json_output[0]->error;
$json_message = $json_error->message;
foreach ($json_output[0]->{'loans'} as $loan) {
echo 'Name: '.$loan->{'name'};
}
?>
You need to tell the web browser what encoding you are giving it.
<?php
header('content-type: text/plain; charset=utf-8');
var_dump(json_decode($jsonStr));
if you are using php 5.4 you may use the function options of json_encode() like this :-
echo $b=json_encode('Dalé',JSON_UNESCAPED_UNICODE);
echo json_decode($b);

Get html source of external webpage without header/encode

I just want to know if its possible to extract content encoded (in utf-8) from a html file without encoding header.
My specific case is this website:
http://www.metal-archives.com/band/discography/id/203/tab/all
I want to extract all the info but, as you can see, this word for example, looks bad:
Motörhead
I tried to use file_get_html, htmlentities, utf_decode, utf_encode and mix of them with different options but I cant find a solution...
Edit:
I just want to see the same website with correct format with this simple code:
$html_discos = file_get_html("http://www.metal-archives.com/band/discography/id/223/tab/all");
//some transform/decode here
print_r($html_discos);
I want the content in correct format in a string or DOM object to get some parts later.
Edit 2:
$file_get_html is a function of "simple html dom" library:
http://simplehtmldom.sourceforge.net/
That have this code:
function file_get_html($url, $use_include_path = false, $context=null, $offset = -1, $maxLen=-1, $lowercase = true, $forceTagsClosed=true, $target_charset = DEFAULT_TARGET_CHARSET, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT)
{
// We DO force the tags to be terminated.
$dom = new simple_html_dom(null, $lowercase, $forceTagsClosed, $target_charset, $stripRN, $defaultBRText, $defaultSpanText);
// For sourceforge users: uncomment the next line and comment the retreive_url_contents line 2 lines down if it is not already done.
$contents = file_get_contents($url, $use_include_path, $context, $offset);
// Paperg - use our own mechanism for getting the contents as we want to control the timeout.
//$contents = retrieve_url_contents($url);
if (empty($contents) || strlen($contents) > MAX_FILE_SIZE)
{
return false;
}
// The second parameter can force the selectors to all be lowercase.
$dom->load($contents, $lowercase, $stripRN);
return $dom;
}
The Content-Type of the URL
http://www.metal-archives.com/band/discography/id/203/tab/all
is:
Content-Type: text/html
This will default to ISO-8859-1. But instead you want to use UTF-8. Change the Content-Type so this is correctly signaled:
Content-Type: text/html; charset=utf-8
See: Setting the HTTP charset parameter
header('Content-Type: text/html; charset=utf-8');
echo file_get_contents('http://www.metal-archives.com/band/discography/id/203/tab/all');
As long as you are emitting as UTF-8, the raw data will work properly.
Try using html_eneity_decode http://php.net/manual/en/function.html-entity-decode.php (the source of that page has encoded characters)

Categories