ダウンロードためにCSVを作る場合、implode()などを使って、変数上でCSVを作成する方法では、値内のダブルコートや改行のエスケープが面倒なためfputcsv()を使うと良い。
fputcsv(リソース,配列)
参考:PHPでCSVを生成する
現実的な方法として、一旦メモリ上にCSVを作成してから出力する方法とCSV形式を都度出力する方法がある。
##一旦メモリ上にCSVを作成してから出力
都度で出力に比べ一旦作ることで、Content-lengthを出力することができるので、ブラウザの「ダウンロード残り時間」を表示できる。
一旦ファイルにCSVを作成してからreadfileで出力する方法は、ディスクIOが大きくパフォーマンスに問題があるので、一旦作るならメモリ上に作成するのが良い。
###PHPでの書きかた
//メモリ上に領域確保
$fp = fopen('php://temp/maxmemory:'.(5*1024*1024),'r+');
$user_list = [
[...],
[...],
[...],
];
foreach($user_list as $user){
fputcsv($fp, $user);
}
header('Content-Type: text/csv');
header("Content-Disposition: attachment; filename=hoge.csv");
//ファイルポインタを先頭へ
rewind($fp);
//リソースを読み込み文字列取得
$csv = stream_get_contents($fp);
//CSVをエクセルで開くことを想定して文字コードをSJIS-winSJISへ
$csv = mb_convert_encoding($csv,'SJIS-win','utf8');
print $csv;
fclose($fp);
php://tempやphp://temp/maxmemoryやphp://memoryを使うことでメモリ上に領域を確保することができる。
メモリリークの危険性を考えると、使用するメモリの上限値を設定できるphp://temp/maxmemoryを使うと良い。
php://temp/maxmemoryは上限値を超えると自動削除される一時ファイルを作る。
mb_convert_encodingで変換するときは、SJISじゃなくてSJIS-winを使う。
参考:SJISじゃなくてSJIS-win、EUC-JPじゃなくてeucJP-winを使おう
###CakePHPでの書きかた
//メモリ上に領域確保
$fp = fopen('php://temp/maxmemory:'.(5*1024*1024),'a');
$user_list = [
[...],
[...],
[...],
];
foreach($user_list as $user){
fputcsv($fp, $user);
}
//ビューを使わない
$this->autoRender = false;
//download()内ではheader("Content-Disposition: attachment; filename=hoge.csv")を行っている
$this->response->download("hoge.csv");
//ファイルポインタを先頭へ
rewind($fp);
//リソースを読み込み文字列を取得する
$csv = stream_get_contents($fp);
//Content-Typeを指定
$this->response->type('csv');
//CSVをエクセルで開くことを想定して文字コードをSJIS-win
$csv = mb_convert_encoding($csv,'SJIS-win','utf8');
$this->response->body($csv);
fclose($fp);
##都度で出力
一旦作成するよりもこちらの方が早い。ただし、Content-lengthを出力できないため、ブラウザの「ダウンロード残り時間」は表示されなくなる。
###PHPの書きかた
header('Content-Type: text/csv');
header("Content-Disposition: attachment; filename=hoge.csv");
$fp = fopen('php://output','w');
//CSVをエクセルで開くことを想定して文字コードをSJISへ変換する設定を行う
stream_filter_append($fp, 'convert.iconv.UTF-8/CP932', STREAM_FILTER_WRITE);
$user_list = [
[...],
[...],
[...],
];
foreach($user_list as $user){
fputcsv($fp, $user);
}
fclose($fp);
###CakePHPの書きかた
//ビューを使わない
$this->autoRender = false;
//Content-Typeを指定
$this->response->type('csv');
//download()内ではheader("Content-Disposition: attachment; filename=hoge.csv")を行っている
$this->response->download("hoge.csv");
$fp = fopen('php://output','w');
//CSVをエクセルで開くことを想定して文字コードをSJISへ変換する設定を行う
stream_filter_append($fp, 'convert.iconv.UTF-8/CP932', STREAM_FILTER_WRITE);
$user_list = [
[...],
[...],
[...],
];
foreach($user_list as $user){
fputcsv($fp, $user);
}
fclose($fp);