26.

Laravel ファイルダウンロード|response()->download / streamDownload / S3 連携

編集
この記事の要点
  • Laravel でファイルをダウンロードさせる方法
  • 基本: return response()->download($filePath)
  • ファイル名を変えるなら ->download($path, "newname.txt")
  • 一時ファイル削除付き: ->deleteFileAfterSend(true)
  • メモリ上のデータをダウンロード: response()->streamDownload(function() {...}, "name.csv")
  • S3 / クラウドストレージは Storage::download(...)

基本: response()->download()

Laravel ではローカルファイルパスを指定するだけでブラウザダウンロードを発生させられます:

public function download() {
    $filePath = public_path('exports/data.csv');
    return response()->download($filePath);
    // Content-Type / Content-Disposition は自動設定される
}

ファイル名を変更

サーバ上のファイル名と異なる名前でダウンロードさせたい場合:

$filePath = storage_path('app/internal_export_20240101_uuid.csv');
return response()->download($filePath, 'ユーザーデータ.csv');
// → ブラウザでは「ユーザーデータ.csv」として保存される

カスタムヘッダ付き

$headers = [
    'Content-Type' => 'text/csv; charset=UTF-8',
    'X-Custom-Header' => 'value',
];
return response()->download($filePath, 'data.csv', $headers);

送信後に一時ファイルを削除

一時ファイルを生成してダウンロードさせる場合、送信後に自動削除すると安全:

$tmpPath = storage_path('app/tmp_' . uniqid() . '.csv');
file_put_contents($tmpPath, $csvContent);

return response()
    ->download($tmpPath, 'export.csv')
    ->deleteFileAfterSend(true);  // ← 送信後に削除

メモリ上のデータをダウンロード: streamDownload

大きな CSV を生成しながら同時にダウンロードさせたい(メモリに全部置きたくない)場合:

return response()->streamDownload(function () {
    $handle = fopen('php://output', 'w');

    // BOM 付き UTF-8(Excel 文字化け対策)
    fwrite($handle, "\xEF\xBB\xBF");

    // ヘッダ行
    fputcsv($handle, ['id', 'name', 'email']);

    // 大量データを 1 行ずつ書き出し(メモリ消費を抑える)
    foreach (User::cursor() as $user) {
        fputcsv($handle, [$user->id, $user->name, $user->email]);
    }

    fclose($handle);
}, 'users.csv', [
    'Content-Type' => 'text/csv; charset=UTF-8',
]);

クラウドストレージ (S3 等) からダウンロード

S3 や FTP に置いたファイルを直接ダウンロードさせる:

use Illuminate\Support\Facades\Storage;

// S3 上のファイルを取得してダウンロード
return Storage::disk('s3')->download('exports/2024/data.csv', 'ユーザーデータ.csv');

// 一時的な公開 URL(プリサインド URL)を発行してリダイレクト
$url = Storage::disk('s3')->temporaryUrl(
    'exports/2024/data.csv',
    now()->addMinutes(5)
);
return redirect($url);  // ブラウザが直接 S3 から取得

ブラウザ表示 (download ではなく view)

PDF や画像を「ダウンロード」ではなく「ブラウザ内で表示」させたい場合:

// inline = ブラウザ内表示、attachment = ダウンロード
return response()->file($filePath, [
    'Content-Type' => 'application/pdf',
    'Content-Disposition' => 'inline; filename="document.pdf"',
]);

// Storage 経由
return Storage::response('path/to/file.pdf');  // inline
return Storage::download('path/to/file.pdf');  // attachment

大容量ファイルの注意

問題対処
PHP のメモリ上限超過php.inimemory_limit を上げる、または streamDownload を使う
実行時間超過 (max_execution_time)set_time_limit(0) をコントローラ先頭で実行
Web サーバのバッファサイズNginx の fastcgi_buffering off / Apache の SendBufferSize
セッションロックダウンロード中に他のリクエストが固まる → session()->save() で先にセッション解放
レンジリクエスト (Resume)Laravel 標準はサポート限定的 → 大容量は S3 直接配信を推奨

セキュリティ

  • ファイルパスのバリデーション必須: ユーザー入力でファイル名を受け取る場合、basename()realpath() でパストラバーサル防止
  • 権限チェック: ダウンロードしてよいユーザーかどうかを Policy / Gate で確認
  • Web ルート外に保存: storage/app/storage/app/private/ に置き、コントローラ経由でのみ配信
  • ファイル拡張子と Content-Type: 実体と異なる拡張子だとブラウザが攻撃を許可してしまうリスク

サンプル: 認証済みユーザー専用ダウンロード

public function download(Order $order) {
    // 認可チェック
    $this->authorize('view', $order);

    $path = $order->invoice_path;
    if (!Storage::exists($path)) {
        abort(404);
    }

    return Storage::download($path, "invoice_{$order->id}.pdf");
}
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. インストールと設定
  2. クイックスタート & チュートリアル(初心者向け)
  3. クイックスタート & チュートリアル(中級者向け)
  4. ルーティング
  5. Bladeテンプレート(ビュー/レイアウト)
  6. コントローラー
  7. マイグレーションとテーブル定義
  8. データベースの設定
  9. Eloquentモデル (ORM)
  10. SQLとクエリビルダー
  11. バリデーション
  12. .envファイルの設定値へのアクセス
  13. 動作環境による分岐処理
  14. configフォルダ配下の設定値へのアクセス
  15. assetヘルパーを利用したpublicフォルダへのアクセス
  16. storageフォルダへのアクセス
  17. アプリケーション名の変更
  18. メンテナンス
  19. ログイン画面(認証システム)の作成
  20. ログインの必須化
  21. ログインユーザー情報の取得
  22. ルートの認証化
  23. 本番サーバーへのデプロイ方法
  24. 多言語化
  25. csrf_field
  26. ファイルのダウンロード
  27. CSVのアップロードおよび読み込み(maatwebsite/excel)
  28. ページタイトルの設定
  29. コマンド一覧
  30. エラー一覧
  31. SQLの実行ログ出力方法
  32. キャッシュのクリア
  33. Selectの結果の最初もしくは最後に任意の値を追加する方法
  34. ajaxでPOST通信する際の注意点
  35. ソーシャルログインの実装
  36. セッション情報の確認
  37. ログイン、ユーザー登録、パスワードリセット後のリダイレクト先の変更方法
  38. redirectやreturn viewにメッセージを付与する方法
  39. クッキー(cookie)の設定と取得
  40. クラスの再読み込み
  41. csrfの有効時間を変更する方法
  42. ViewComposerを用いてviewに共通の値を付与する方法
  43. View::shareを用いて共通の値を各ビューに渡す方法
  44. ミドルウェアを用いた処理の共通化
  45. Middleware内でAuth::check()などを使用する方法
  46. Controller以外でリダイレクトする方法
  47. セッションの値の取得/保存/更新/削除
  48. $requestの値を変更する方法
  49. 常時SSL化
  50. ページング(ページネーション)をする方法
  51. vue.jsとの連携
  52. Vue.jsと連携するSPA実行環境構築
  53. .envの値をvue.jsで参照する方法
  54. vue.jsを本番環境にリリースする方法
  55. could not find driver(Windows, MySQL編)