Laravelでcsvファイルをダウンロードできるようにする

受付中

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 307

tanukitii

score 44

追記:
hentaimanさんからご教示いただいたURLから,csvファイルを取得するためのツールをダウンロードし,使ってみたところ,私がやりたいことが実現できました。
ただ,現在,多くの方がこれ以外で,別の解決策を提示してくださっているので,それらも検証してみようかと思います。その結果次第で,ベストアンサーを決めようかと思います。
お待たせしてしまうかもしれませんが,しばらくお待ちくださいますようよろしくお願いいたします。


質問

途中までコードを書いていて,いろいろ試した結果何をどうすればよいのか分からなくなってしまいました。
下記に,コードの一部をまとめますので,どうしたらcsvファイルをダウンロードできるようになるかご教示いただけると幸いです。

Route::get('users/csv', 'csvController@download'); // <-これ
Route::resource('users', 'UserController');
class csvController extends Controller
{
    //2つのテーブル(tbl_userとtbl_way)を結合し,tbl_userのidとname,tbl_wayのway_nameを
  //csvファイルで取得する。
    public function download( Request $request )
    {
        //テーブルの結合
        $users = DB::table('tbl_users')
                    ->leftJoin('tbl_ways', 'tbl_users.way_id', '=', 'tbl_ways.id')
                    ->get();

        $response = new StreamedResponse (function() use ($request){
  
       //ファイルを開く
            $stream = fopen('php://output', 'w');

            // 文字化け回避
            stream_filter_prepend($stream,'convert.iconv.utf-8/cp932//TRANSLIT');

            // タイトルを追加
            fputcsv($stream, ['id', '氏名', '移動手段']);

            foreach ($users as $user) {
                fputcsv($stream, [$user->id, $user->name, $user->way_name]);
            };

       //ファイルを閉じる
            fclose($stream);
        });

     //ここから下のコードについては何かよくわかっていない。
        //特に何をreturnすればよいか分からない。
        $response->headers->set('Content-Type', 'application/octet-stream');
        $response->headers->set('Content-Disposition', 
                  'attachment; filename="ファイル名.csv"');
        return view('users/index');
    }
}

index.blade.phpでボタンを設置し,クリックした際にcsvファイルがダウンロードできるようにします。
そのとき,現在いるぺージからは遷移しないようにする予定でした。

<form action="users/csv" method="get">
    <input type="submit" value="csv">
</form>

補足説明

テーブルは下記の2つ。tbl_wayがtbl_userのマスターテーブルで,idとway_idが紐づいています。
tbl_user

id name way_id
1 taro 1
2 yamada 2
3 ziro 1

tbl_way

id way_name
1 train
2 walk

なお,csvファイルでダウンロードした際,下記のような結果を期待しております。

id 氏名 移動手段
1 taro train
2 yamada walk
3 ziro train

以上です。
もし,どなたか分かれば,ご教示いただけると幸いです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+2

csvの話だけなら、他の人が書いてあるようにライブラリを使えばいいと思うんですが、それだと質問の趣旨と変わってしまいそうなので・・・

ここから下のコードについては何かよくわかっていない。

$response->headers->set('Content-Type', 'application/octet-stream');

レスポンスヘッダに、コンテンツタイプがダウンロードコンテンツ(application/octet-stream)ということをを設定します。
csvなら text/csv でもよいかと思います。

$response->headers->set('Content-Disposition', 
                  'attachment; filename="ファイル名.csv"');

レスポンスヘッダに、Content-Dispositionを設定します。
書いてある通りファイル名をセットします。

特に何をreturnすればよいか分からない。

$responseをリターンしてください。


HTTPレスポンスにはheaderとbodyがあるのですが
HTML等が入っっている部分がbody、どこのサーバが返した、コンテンツのタイプなどを管理しているのが、headerです。

headerとbodyをまとめて管理しているのが、Responseで、その中でもストリーム処理ができるresponseクラスがStreamedResponseという感じです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/27 18:13

    あとコールバック用の関数のuseにusersがないので実行時に落ちる気がします。 use($request, $users)にすればこと無きを得る気がしますが
    詳細は`無名関数`とか、`即時関数`で調べてみてください。

    キャンセル

  • 2019/06/27 23:48

    ご回答いただき,誠にありがとうございます。

    >HTTPレスポンスにはheaderとbodyがあるのですが
    久々にそのワードを聞きました。昔ちらっと触れたきりだったので,忘れてしまっていました。
    これを機に復習してみようかと思います。

    また,後ほど,mikkameさんからご教示いただいたことを検証してみようかと思います。

    キャンセル

  • 2019/07/02 15:48 編集

    検証するための時間がとれなくて,ご連絡が遅くなってしまいました。
    申し訳ございません。
    return $response;も試してみたところ,下記URLのぺージに遷移するのですが,
    localhost:8000/users/csv?
    「このサイトにアクセスできません」と表示されます。
    returnに渡すものも原因かと思いますが,それ以外でも何か原因があるかもしれません。
    もし,心当たりがあれば,ご教示いただけると幸いです。
    宜しくお願いいたします。

    キャンセル

+1

特に自前でコードを書くことに拘らないのならテキトーなもの追加して使うといいですよ。
検索したらパッと二つヒットすると思うけど、特に簡単で使いやすかった方紹介します。
fast-excel
名前にはエクセルって書いてあるけどcsvも出せます

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/27 23:27

    あ,できました!
    Controllerにdownloadメソッドを記述し,web.phpに
    Route::get('users/csv', 'csvController@downloadCSV');
    を追加することで,現在いるぺージから移動することなくcsvファイルをダウンロードできました。

    ただ,web.phpのgetで,users/csvとしているのにURLが変化しない理由がまだわかっていないので,
    コードを見てみようと思います。

    キャンセル

  • 2019/06/28 00:38

    URLが変化しないの理由ってのは、ダウンロードボタン(?)を押したのにブラウザのアドレスバーが変わらないのにCSVダウンロード出来てしまうのは何で?って意味ですか?
    もしそういういう意味で言ってるならそれが普通の動作です。そこら辺のサイトでzipとかDLする時にアドレスバーにはzipとか表示されないのと同じことです。
    そういう意味じゃないんなら、ちょっと何言ってるか分かんないな・・・

    キャンセル

  • 2019/06/29 12:59

    現在いるページがlocalhost:8000/usersで,ダウンロードボタンをクリックすると,
    web.phpのRoute::get('users/csv', 'csvController@downloadCSV');
    が呼び出されるので,URLがlocalhost:8000/users/csvに変わってしまうのではないかと思っていたのですが,ボタンをクリックしても,localhost:8000/usersのままなので,どうしてだろう?

    という意味でした。分かりにくくて申し訳ないです。

    キャンセル

+1

実際に使ってるのはこの辺だけど自分で試して選ぶしかない。
https://github.com/thephpleague/csv
https://github.com/Maatwebsite/Laravel-Excel

Laravel用はしばらく使ってみないと分からないことも多い。
composerでこんな指定してるとバージョンアップ時にすぐ対応してくれるかどうかが重要。

"illuminate/support": "5.5.*|5.6.*|5.7.*|5.8.*",
"illuminate/support": "5.3.* || 5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.*",

5.9出てもこのままだとインストールできないので対応待ちの時間ができる。
^5.5で指定してくれればいいのに。
バージョンアップへの対応がないとか遅いせいで使わなくなったパッケージが大量にある。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/26 22:21

    早速のご回答誠にありがとうございます。

    自分実装は勉強のためにとどめておき,実際に何かアプリケーションを作る際は,
    できるだけ,すでに用意されているものを使用するというのが一般的なんですね。
    いろいろ試してみようかと思います。
    ちなみに私が使用しているLaravelのバージョンは5.8.23です。

    キャンセル

+1

//特に何をreturnすればよいか分からない。

return;で良いと思います。
レスポンスヘッダにapplication/octet-streamを指定した時点でブラウザへの画面出力は発生しないわけなので。
実際にPure PHPでダウンロード処理を作るときもreadfile(ファイルパス);したあとにexit;で終わることもあります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/02 15:49

    と思ったらmikkameさんの回答にありましたね。

    キャンセル

  • 2019/07/02 18:48

    はい。
    で,それも試してみたのですが,問題を解消できないという状況です。
    他に理由があるのでしょうね…(私の勉強不足でしょうね…)

    キャンセル

  • 2019/07/02 19:05

    回答者の意図通りに組めているかまでは分からないので現在のコードと起きている現象を質問に追記してください(流れもあるので更新ではなく追記でお願いします)

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる