春一番が吹いてあったかくなったと思ったらすぐ真冬並みの気候で雨ですか...
北側に面した非常に冷えやすい自宅の環境 でこちらのポストを書いています。

記事作成当日の2024年2月23日の天気

記事作成当日の2024年2月23日の天気

まだしばらくは暖房は手放せそう ですね。。

そんな今日この頃ですが、Hackerの皆様はいかがお過ごしでございましょうか。

おさらい

前回の連載ではVaporの「ストレージ」をプロジェクトにアタッチ して、疎通ができるところまで進めました。

第5回の今回は、追加した「ストレージ」に画面からCSVファイルをアップロードできる処理を追加し、実際に「ストレージ」のバックボーンとなっているAWS S3にファイルがアップロードされていることを確認します。

またそのファイルをダウンロードできる処理も作り、仮想環境の一種であり、一般のWebサーバのように保存領域がないVapor上でも、「ストレージ」を介して ファイルの入出力を恒久的に行える ところを確認していきます。

前提

実装

実装時はローカルの開発環境を使う

すでにここまでの連載で、 productionブランチにコミットをすれば自動的にVaporに 更新した処理が反映される のを確認しています。

しかし、毎回ファイルを修正するたびにデプロイを行なっていてはあまりに効率が悪すぎるので、前回の第3回でご説明をした手順で ローカルのサーバを立ち上げて確認をしていきます。

php artisan serve
簡単に簡易Webサーバの立ち上げが可能

簡単に簡易Webサーバの立ち上げが可能

ローカルの開発環境を使う場合の問題点

vaporのストレージは、裏側はAWS S3のバケットであることはここまでご説明してきました。かつ、ファイルアップロードを行う際にはAWS Lambda (サーバレスで関数単位での処理を行ってくれるAWSのサービスのうちの一つ)を経由し、ファイル容量の上限は4.5MBに制限される

のはVaporの公式ドキュメントのStorageのページで確認しました。

これを具体的にどうやっているかですが、Vaporのアプリデプロイ時に設定を環境変数として割り当て、自動でアップロードできるようになる設定してくれるだろうと思っていました。

ローカルから自動的にS3にアップロードしてくれる仕組みはVaporにはない

しかし、今回のように

かつ

には

従来のLaravelプロジェクトと同様に.envにAWS S3への疎通設定を するしかない、というポスト を見つけました。(以下の参考記事URL様)

参考記事: HOW TO UPLOAD FILES WITH LARAVEL VAPOR?

Note: But when you try in your local file system the file only gets placed in the storage directory since the local file system may have the default driver as ‘local’. You can configure your ‘.env’ file with the AWS configurations and default filesystem as S3 if you want to place the file in the S3 bucket locally.

注意してください:しかし、ローカルファイルシステムで試すと、ローカルファイルシステムのデフォルトドライバが'local'になっている可能性があるため、ファイルはストレージディレクトリにしか置かれない。ローカルにあるS3バケットにファイルを置きたい場合は、AWSの設定とデフォルトのファイルシステムを'.env'ファイルに設定することができる。

今回はテスト用途ということもあり、そのままS3ではなくファイルストレージを使う形で進めますが、商用であれば、テスト用のバケットを作成し、AWS IAMのシークレットトークン・アクセスキーを発行して利用すると思います。

その点の違いがあるので予めご承知おきください。

アップロード処理の実装

以下のように各ファイルを変更していきます。トップページはそのままで、リンクがなく見えないところにテスト用の処理を作っていく イメージです

app/Http/Controllers/UploadController.php

app/Http/Controllers/UploadController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class UploadController extends Controller
{
  public function index()
  {
    return view('upload.index');
  }

  public function save(Request $request)
  {

    if ($request->hasFile('file')) {
      $file = $request->file('file');

      // S3にファイルをアップロード
      $filePath = Storage::put("data", $file);

      return view('upload.complete', compact('filePath'));
    }
    return view('upload.index');
  }

  public function download(Request $request)
  {
    $filePath = $request->filePath;

    if (Storage::exists($filePath)) {
      $file = Storage::get($filePath);
      $fileName = basename($filePath);

      // ダウンロード処理
      return response()->streamDownload(function () use ($file) {
        echo $file;
      }, $fileName);
    } else {
      // TODO: エラー処理を適切にする。
      abort(404);
    }
  }
}

routes/web.php

routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UploadController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Route::get('/upload', [UploadController::class, 'index'])->name('upload');
Route::post('/upload/save', [UploadController::class, 'save'])->name('upload.save');
Route::get('/upload/download', [UploadController::class, 'download'])->name('upload.download');

resources/views/upload/index.blade.php

resources/view/upload/index.blade.php
<h1>アップロードテスト</h1>

<form method="POST" action="{{route('upload.save')}}" enctype="multipart/form-data">
  @csrf
  <input type="file" name="file" />
  <button type="submit">送信する</button>
</form>

resources/view/upload/complete.blade.php

resources/view/upload/complete.blade.php
<h1>アップロード完了</h1>

<p>無事アップロードを行いました。</p>
<p>アップロードしたパスは{{$filePath}}です。</p>

<h2>今あげたファイルをダウンロード</h2>
<p>ダウンロードしたい時はこちらをクリックしてください。</p>

<form method="GET" action="{{route('upload.download')}}" enctype="multipart/form-data">
  @csrf
  <input type="hidden" name="filePath" value="{{$filePath}}" />
  <button type="submit">ダウンロード</button>
</form>

<a href="{{route('upload')}}">戻る</a>

以上のソースコードを、productionブランチにコミットすると、Vaporにリリースされます

動作確認

ここまでの作業を終えたら、実際に画面で確認します。

Vaporのトップページから、割り当てられているURLを確認しアクセス。

vp503.png

larvelのデフォルトの画面が開くので、/uploadを末尾につけたURLにアドレスバーからアクセス します。

vp504.png

「アップロードテスト」 と出るので、テスト用CSVファイルを選択。

vp505.png

※ なお、テストに用いるのみのため、CSVの内容はなんでも良いかとおもいます、もしテスト用のCSVがない場合は以下の内容をメモ帳に貼り付けて、test.csvとしてファイル名をリネームして使ってみてください。

id,name,type,category,created_at,updated_at
1,yuta,1,0,2024-02-23 19:50:00,2024-02-23 19:50:00
2,hiroshi,1,1,2024-02-23 19:50:00,2024-02-23 19:50:00
3,akira,2,0,2024-02-23 19:50:00,2024-02-23 19:50:00
4,taro,2,1,2024-02-23 19:50:00,2024-02-23 19:50:00
5,goro,2,2,2024-02-23 19:50:00,2024-02-23 19:50:00
vp506.png

アップロードが無事完了するとダウンロードボタンが表示されるので、ダウンロードしてみます。

vp507.png

ダウンロードできました。ダウンロードしたファイル名をAWS S3に移動して直接確認します。

vp508.png

想定通りアップロードができていました。 何も設定していないのに、気軽にクラウドストレージが使えてスゴい...。

まとめ

確かに、Laravel側で特段の設定なくS3に対してファイルアップロードができてしまったのはスゴい なと思いました。

商用利用で、一定の予算が最初から確保できるようなシステムであれば(例えば月額数千円インフラにかけられるのであれば)最初からVaporの利用を考えるのもありだなとこの機能を使ってみて思いました。


いったんVaporのご紹介については本記事で最終回 といたします。

ご愛顧いただき、また最後までお読みをいただきましてありがとうございました。


引き続き連載記事などやPHP/Next.jsなどに関する便利なTipsを発信していければと思いますので、ご愛読、またXへのDMなどもお待ちしております。引き続きモルドスプーンブログ をよろしくお願いいたします。