PHP
CakePHP

【PHP】【CakePHP】CSVダウンロード

More than 3 years have passed since last update.

ダウンロードためにCSVを作る場合、implode()などを使って、変数上でCSVを作成する方法では、値内のダブルコートや改行のエスケープが面倒なためfputcsv()を使うと良い。


php

fputcsv(リソース,配列)


参考:PHPでCSVを生成する

現実的な方法として、一旦メモリ上にCSVを作成してから出力する方法とCSV形式を都度出力する方法がある。


一旦メモリ上にCSVを作成してから出力

都度で出力に比べ一旦作ることで、Content-lengthを出力することができるので、ブラウザの「ダウンロード残り時間」を表示できる。

一旦ファイルにCSVを作成してからreadfileで出力する方法は、ディスクIOが大きくパフォーマンスに問題があるので、一旦作るならメモリ上に作成するのが良い。


PHPでの書きかた


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は上限値を超えると自動削除される一時ファイルを作る。

参考:PHPで一時的なファイルポインタを扱う方法

mb_convert_encodingで変換するときは、SJISじゃなくてSJIS-winを使う。

参考:SJISじゃなくてSJIS-win、EUC-JPじゃなくてeucJP-winを使おう


CakePHPでの書きかた


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);


参考:【CakePHP】ファイルダウンロード


都度で出力

一旦作成するよりもこちらの方が早い。ただし、Content-lengthを出力できないため、ブラウザの「ダウンロード残り時間」は表示されなくなる。


PHPの書きかた


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の書きかた


php


//ビューを使わない
$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);