0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Laravel Inertia】ファイルのダウンロードがしたいなぁ

Last updated at Posted at 2025-12-09

今までデータのやり取りを解説してきましたが、システムを扱うとなると避けられない強敵。ファイルの取り扱いです。

テーブルの CSV を吐き出したい... PDF を吐き出したい、、、
など要求は多岐にわたると思います。

そんなものたちも、VILT スタックの中で扱うことができます。
大きく二種類ご紹介します。

ファイルをダウンロードさせる場合

用意した PDF ファイルをダウンロードさせたい場面を考えます。
何かしらの PDFstorage/app/private/sample.pdf に配置してください。

それでは、ダウンロードに利用するルートを作成します。
ポイントは GET で行うことです。

routes/web.php
  Route::get('/test', [TestController::class, 'index'])->name('tests.index');
+ Route::get('/test/download', [TestController::class, 'download'])->name('tests.download');

ではファイルを取得してダウンロードさせます。

既に用意したファイルをダウンロードする場合は、 response()->file() を利用します。
リダイレクトや Inertia は必要ありません。

Content-Type にファイルのMIME属性、Content-Disposition にファイル名を記載します。日本語も大丈夫です。

app/Http/Controllers/TestController.php
namespace App\Http\Controllers;

use Inertia\Inertia;

class TestController extends Controller
{
    public function index()
    {
        return Inertia::render('Test');
    }

+   public function download()
+   {
+       $path = storage_path('app/private/sample.pdf');
+        return response()->file($path, [
+           'Content-Type' => 'application/pdf',
+           'Content-Disposition' => 'attachment; filename="テストファイル.pdf"',
+       ]);
+   }
}

フロント側は <a> リンクと同じような形で、処理を実装します。
別タブで開かせたい場合は window.open() を使いましょう。

resources/js/pages/Test.vue
<template>
  <div class="m-4 flex gap-2">
    <Button as="a" :href="download().url" label="直接ダウンロード" />

    <Button label="ダウンロード" @click="onDownload" />
  </div>
</template>

<script setup lang="ts">
import Button from 'primevue/button'

import { download } from '@/routes/tests'

const onDownload = () => {
  window.open(download().url, '_blank')
}
</script>

こんな感じですね。

image.png

ボタンを押すと直接ダウンロード処理が実行されます。

image.png

PDf などブラウザが直接表示に対応しているものは inline 表示もできます。

app/Http/Controllers/TestController.php
        return response()->file($path, [
            'Content-Type' => 'application/pdf',
-           'Content-Disposition' => 'attachment; filename="テストファイル.pdf"',
+           'Content-Disposition' => 'inline; filename="テストファイル.pdf"',
        ]);

image.png

inline のときは、window.open() がおススメですが、直接ダウンロードなら、どっちでもいいと思いますね。ただ処理のエラーが出ると Inertia ごとエラー表示となるので、別タブ利用がおススメです。

その場でファイルを生成する場合

これはボタンを押したときにデータベースの値を加工して、ファイルとして出力したい場合ですね。
画像の加工などもこちらのバターンになります。

では分かりやすいように Excel を生成してみます。
生成には PhpSpreadsheet を利用します。

$ composer require phpoffice/phpspreadsheet

そしたら、Excel生成はサンプルをそのまま持ってきます。
その場で生成する場合は response()->streamDownload() というものを利用します。
これは PHP のストリーム(書き込み先)を通信に直接つなぐ方法です。

app/Http/Controllers/TestController.php
    public function download()
    {
        $spreadsheet = new Spreadsheet();
        $activeWorksheet = $spreadsheet->getActiveSheet();
        $activeWorksheet->setCellValue('A1', 'Hello World !');

        $writer = new Xlsx($spreadsheet);

        return response()->streamDownload(function () use ($writer) {
            $writer->save('php://output');
        }, null, [
            'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'Content-Disposition' => 'attachment; filename="hello_world.xlsx"',
        ]);
    }

ボタンを押してみると、Excel ファイルが落ちてくると思います。

image.png

stream で返すことで一時ファイルの処理などいらずで、動的生成が簡単に行えます。
メモリ効率も良いため、いいことだらけです。

ファイル生成ごときでバッジ処理はしたくないんじゃ~
管理項目が増えすぎてバグの元。。。

おわりに

ファイルを扱う際は、あえて Inertia.js の外に出るのがポイントです。
ここさえ気を付ければ、Laravel のお作法通りに利用できます!

なので POST に変えたい場合もいい感じに実現できそうですよね。

あ、ちなみに PDF 出力に関しては、過去記事を書いているので一読の価値ありです!
もう去年の記事だって。と気が進むのは早い...。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?