暖かくなってきたと思ったら、また寒波と雨が降ってる東京、テンション下がるよね。。てことで、こんにちわ、Hashiです。今回は、Laravelで大量のデータをcsvとしてダウンロードするやり方を備忘録も兼ねて紹介したいと思います。大体、システム開発をする際はcsv等のダウンロード機能は求められることが多いですよね。
なお、Laravelとはなんぞやという方は下の記事を参照してください!
環境
今回の環境は下記の通りとなります。
今回の環境
- Laravel 5.5
- MySQL 5.6.37
大容量データをcsvでダウンロードする
さて、早速、具体的にコードを書いていきますが、今回は下記の用法で実装してみたいと思います。
- Symfonyの「StreamedResponse」クラスを使用する
- fputcsvを使用する
- php://outputストリームを使用する
- chunkでデータを分割して取得する
それぞれの用途理由は下記の通りです。
Symfonyの「StreamedResponse」クラスを使用する
SymfonyのStreamedResponseクラスは、第一引数に文字列ではなくコールバック関数として出力内容を記述することができるのが特徴です。さらに第3引数にレスポンスヘッダの設定もできるので、CSV等のデータダウンロードの際にはとても便利です。
fputcsvを使用する
fputcsvは、csv形式にフォーマットしてくれるPHPお決まりの関数ですね。
php://outputストリームを使用する
fputcsvを使用する場合は、fopenまたはfsockopen関数によって正常にオープンされたファイルを指している必要があるとphpの公式サイトでは説明されていますが、今回はいちいちファイルに書きださなくても出力ストリームとして「php://output」を使用して、ファイルに書き出すことなくダウンロードしたいと思います。
chunkでデータを分割して取得する
今回は、大容量のデータを扱うことを前提としていますので、chunkを使用してデータを分割取得する必要があります。これによってメモリーリークを防ぐことができます。
ということで、実際にサンプルコードを書いていきたいと思います。なお、ここでは下記のような状態を想定します。
- usersテーブルに大量のデータがある
- usersテーブルには、idとnameフィールドが存在する
- usersテーブルからid昇順で全件取得する
- usersテーブルはUserモデルと紐づいている
- chunkで1,000件ずつ分割して取得する
まずは、StreamedResponseクラスを使用するので、ライブラリをimportします。
use Symfony\Component\HttpFoundation\StreamedResponse;
次に、実際のダウンロード部分のソースになります。
public function downloadCSV() { return new StreamedResponse( function () { $stream = fopen('php://output', 'w'); User::orderBy('id','asc')->chunk(1000, function ($users) use ($stream) { foreach ($users as $user) { fputcsv($stream, [$user->id, $user->name]); } }); fclose($stream); }, 200, [ 'Content-Type' => 'text/csv', 'Content-Disposition' => 'attachment; filename="users.csv"', ] ); }
StreamedResponseの第一引数部分が実際にデータベースからデータを1,000件出力して、fputcsv関数でcsvにフォーマットしている箇所になります。そして、第3引数においてレスポンスヘッダを指定しています。
これで大容量のデータでもメモリリークを起こさずにcsvのダウンロードをすることができます。
以上、今回はここまで!Takaでした。また!
この記事のまとめ
- Laravelで容量の大きいデータをcsvでダウンロードする方法