laravel8で、何十万件もあるレコードをCSVファイルとしてダウンロード出来るようにしてみた。
$query->cursor()を使って1レコードずつ処理して、ストリームに流し込む(サーバ側でCSVファイルは作らない)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// コールバック関数に1行ずつ書き込んでいく処理を記述 $callback = function () use ($query) { // 出力バッファをopen $stream = fopen('php://output', 'w'); // 文字コードをShift-JISに変換 stream_filter_prepend($stream, 'convert.iconv.utf-8/cp932//TRANSLIT'); // 1行目にCSVヘッダを書き込む(日本語) self::mb_fputcsv($stream, self::CSV_HEADER_JAPANESE); //CSV 2行目以降のレコード・データ // cursor()メソッドで1レコードずつストリームに流す処理を実現できる。 foreach ($query->cursor() as $floor) { // cursorは1回のSQLで全レコードを取得するので速いけどメモリ不足の可能性があるので、その場合はchunkで1000件ずつ取得(遅くなるけどメモリは少なくなる) // $query->chunk(1000, function ($floors) use ($stream) { // foreach ($floors as $floor) { $arr_floor = []; //初期化 // 日本語ヘッダと同じ並びのカラム名で指定 for($i=0; $i<count(self::CSV_HEADER); $i++){ $arr_floor[] = $floor[self::CSV_HEADER[$i]]; } self::mb_fputcsv($stream,$arr_floor); } fclose($stream); }; // 保存するファイル名 $filename = date('Ymd_His') . '_floors.csv'; // ファイルダウンロードさせるために、ヘッダー出力を調整 $header = [ 'Content-Type' => 'text/csv', ]; return response()->streamDownload($callback, $filename, $header); |
laravel5.1でExcel/CSVを取り扱う方法(laravel-excelというパッケージを使おうと思ったけど、結局コーディングだけで実装した)