ブラウザからTSVファイルをローカルダウンロードする方法【Symfony/Laravel】
DBのデータを、ブラウザ経由でローカルにダウンロードする方法です。TSVでダウンロードし、エクセルで表示できるようにしました。備忘録として残しておきます。
やりたいこと
このデータを加工してエクセルで表示させたい。
$fruits = [
[
"name" => "apple",
"price" => "120"
],
[
"name" => "banana",
"price" => "78"
],
[
"name" => "orange",
"price" => "50"
]
];
工程としては、ブラウザで https://xxxxxxx.jp/download を叩くとローカルにTSVファイルがダウンロードされ、
TSVファイルをExcelから開くと、このように表示されることを想定。
ルーティングの設定
Route::get('/sample/download', 'Sample\SampleController@download');
StreamedResponseを使ってストリームを作成
StreamedResponseはSymfony(PHPフレームワーク)のクラスです。ダウンロードすることなく、標準でLaravelに入っているようです。
StreamedResponseクラスを使うとクライアントにレスポンスをストリーミングできます。
use Symfony\Component\HttpFoundation\StreamedResponse;
(中略)
public function download() {
// ストリームを作成
$streamResponse = new StreamedResponse();
// 実際はDBからデータを取得
$fruits = [
[
"name" => "apple",
"price" => "120"
],
[
"name" => "banana",
"price" => "78"
],
[
"name" => "orange",
"price" => "50"
]
];
// tsvデータを設定
$streamResponse->setCallback(function () use($fruits) {
$this->createTsvData($fruits);
});
}
setCallbackにtsvにしたいデータを設定します。データの生成はcreateTsvDataメソッドで行います。
createTsvDataメソッドにfruitsのデータを渡します。
TSVデータの生成
TSVファイルに書き込むデータをPHPのファイルシステム関数を使って実装していきます。
private function createTsvData($fruits) {
$tsvFile = fopen('php://output', 'w');
// tsv1行目
$columnNames = [
'名前',
'値段'
];
// Excelに対応するため文字コード変換
mb_convert_variables('SJIS-win', 'UTF-8', $columnNames);
// ファイルへ追記
fputcsv($tsvFile, $columnNames, "\t");
foreach ($fruits as $fruit) {
$row = [
$fruit['name'],
$fruit['price']
];
// Excelに対応するため文字コード変換
mb_convert_variables('SJIS-win', 'UTF-8', $row);
// ファイルへ追記
fputcsv($tsvFile, $row, "\t");
}
// ファイルデータ作成終了
fclose($tsvFile);
}
使用しているPHPの関数はこんな役割があります。
・fopen
URLをオープンにする。'w' は書き出しのみでオープン。
・mb_convert_variables
文字コードを変換。
第一引数・・・変換後の文字コード
第二引数・・・変換前の文字コード
第三引数・・・変換対象
(SJIS-winは、SJISに特殊文字を追加した文字コード)
・fputcsv
行を CSV 形式にフォーマットして、ファイルポインタに書き込む
第一引数・・・対象ファイル
第二引数・・・1行の配列データ
第三引数・・・オプション。区切り文字を指定
( "\t" を指定するとタブ区切りのtsvになる。csvにしたい場合は指定なしでOK)
・fclose
オープンされたファイルポインタをクローズ
ストリームを返す
tsvデータの作成・設定ができたので、ヘッダ・ステータスコードをストリームに設定します。
public function download() {
(中略)
// 本日の日付をファイル名にする
$now = Carbon::now();
$today = $now->format('Ymd');
$fileName = "fruits_${today}.tsv";
// ヘッダを設定
$streamResponse->headers->set('Content-Type', 'text/tab-separated-values');
$streamResponse->headers->set('Content-Disposition', "attachment; filename=${fileName}");
// ステータスコードを設定
$streamResponse->setStatusCode(200);
$streamResponse->send();
return $streamResponse;
}
StreamedResponseへの設定方法は下記です。
・ヘッダの設定
$streamResponse->headers->set();
・ステータスコードの設定
$streamResponse->setStatusCode();
参考
ブラウザからURLを叩いてみる
処理が書けたので検証してみます。
作成したtsvファイルをダウンロードできました。
エクセルから開くとこのように表示されます。
完成です🎉
完成したControllerの全コード
<?php
namespace App\Http\Controllers\Sample;
use App\Http\Controllers\Controller;
use Carbon\Carbon;
use Symfony\Component\HttpFoundation\StreamedResponse;
class SampleController extends Controller
{
/**
* ブラウザからファイルをダウンロード
*/
public function download() {
// ストリームを作成
$streamResponse = new StreamedResponse();
$fruits = [
[
"name" => "apple",
"price" => "120"
],
[
"name" => "banana",
"price" => "78"
],
[
"name" => "orange",
"price" => "50"
]
];
// tsvデータを設定
$streamResponse->setCallback(function () use($fruits) {
$this->createTsvData($fruits);
});
// 本日の日付をファイル名にする
$now = Carbon::now();
$today = $now->format('Ymd');
$fileName = "fruits_${today}.tsv";
// ヘッダを設定
$streamResponse->headers->set('Content-Type', 'text/tab-separated-values');
$streamResponse->headers->set('Content-Disposition', "attachment; filename=${fileName}");
// ステータスコードを設定
$streamResponse->setStatusCode(200);
$streamResponse->send();
return $streamResponse;
}
/**
* tsvファイルのデータを作成
*/
private function createTsvData($fruits) {
$tsvFile = fopen('php://output', 'w');
// tsv1行目
$columnNames = [
'名前',
'値段'
];
// Excelに対応するため文字コード変換
mb_convert_variables('SJIS-win', 'UTF-8', $columnNames);
// ファイルへ追記
fputcsv($tsvFile, $columnNames, "\t");
foreach ($fruits as $fruit) {
$row = [
$fruit['name'],
$fruit['price']
];
// Excelに対応するため文字コード変換
mb_convert_variables('SJIS-win', 'UTF-8', $row);
// ファイルへ追記
fputcsv($tsvFile, $row, "\t");
}
// ファイルデータ作成終了
fclose($tsvFile);
}
}
いいなと思ったら応援しよう!
スキ頂けると嬉しいです〜