Laravelのファイルアップロード——全体の流れ
ファイルアップロードの実装は以下の流れで行います。
- フロントエンド(HTMLフォーム)でファイルを選択して送信
- LaravelのFormRequestでバリデーション
Storageファサードで保存- DBにパスを記録
- URLを生成して返す
HTMLフォームの設定
<!-- enctype="multipart/form-data" が必須 -->
<form action="/upload" method="POST" enctype="multipart/form-data">
@csrf
<input type="file"
name="avatar"
accept="image/jpeg,image/png,image/webp"
class="block w-full text-sm text-gray-600
file:mr-4 file:py-2 file:px-4 file:rounded-full
file:border-0 file:text-sm file:font-medium
file:bg-amber-50 file:text-amber-700
hover:file:bg-amber-100">
<button type="submit" class="mt-4 px-6 py-2 bg-amber-700 text-white rounded-full">
アップロード
</button>
</form>
バリデーション
class UploadAvatarRequest extends FormRequest
{
public function rules(): array
{
return [
'avatar' => [
'required',
'file',
'image', // MIME: jpeg, png, gif, bmp, webp, svg
'mimes:jpeg,png,webp', // 許可する拡張子
'max:5120', // 5MB以内(KBで指定)
'dimensions:min_width=100,min_height=100,max_width=2000,max_height=2000',
],
];
}
public function messages(): array
{
return [
'avatar.required' => 'ファイルを選択してください',
'avatar.image' => '画像ファイルを選択してください',
'avatar.mimes' => 'JPEG・PNG・WebP形式のみ対応しています',
'avatar.max' => 'ファイルサイズは5MB以内にしてください',
'avatar.dimensions' => '画像サイズは100px〜2000px以内にしてください',
];
}
}
コントローラーでの保存
class UserAvatarController extends Controller
{
public function store(UploadAvatarRequest $request): JsonResponse
{
$user = $request->user();
// 既存のアバターを削除
if ($user->avatar_path) {
Storage::disk('public')->delete($user->avatar_path);
}
// ファイルを保存(ユニークなファイル名を自動生成)
$path = $request->file('avatar')
->store("avatars/{$user->id}", 'public');
// → storage/app/public/avatars/1/xxxxxxxx.jpg
// DBに保存
$user->update(['avatar_path' => $path]);
// 公開URLを返す
$url = Storage::disk('public')->url($path);
return response()->json([
'message' => 'アバターを更新しました',
'url' => $url,
]);
}
}
ファイル名のカスタマイズ
use Illuminate\Support\Str;
// カスタムファイル名で保存
$extension = $request->file('avatar')->extension();
$filename = Str::uuid() . '.' . $extension;
$path = $request->file('avatar')
->storeAs("avatars/{$user->id}", $filename, 'public');
画像のリサイズ処理
アップロード後にリサイズすることで、ストレージの節約とページ表示速度の向上が図れます。
composer require intervention/image
use Intervention\Image\Facades\Image;
class UserAvatarController extends Controller
{
public function store(UploadAvatarRequest $request): JsonResponse
{
$file = $request->file('avatar');
// リサイズ処理
$image = Image::make($file->getRealPath())
->fit(400, 400) // 400x400にクロップ
->encode('webp', 85); // WebPに変換(品質85%)
$filename = Str::uuid() . '.webp';
$path = "avatars/{$request->user()->id}/{$filename}";
Storage::disk('public')->put($path, $image->getEncoded());
$request->user()->update(['avatar_path' => $path]);
return response()->json([
'url' => Storage::disk('public')->url($path),
]);
}
}
複数ファイルのアップロード
// バリデーション
'photos' => ['required', 'array', 'max:10'],
'photos.*' => ['image', 'mimes:jpeg,png,webp', 'max:5120'],
// 保存
$paths = [];
foreach ($request->file('photos') as $photo) {
$paths[] = $photo->store("listings/{$listingId}/photos", 'public');
}
// DBに一括保存
$listingPhotos = array_map(fn($path) => [
'listing_id' => $listingId,
'path' => $path,
'url' => Storage::disk('public')->url($path),
'created_at' => now(),
'updated_at' => now(),
], $paths);
DB::table('listing_photos')->insert($listingPhotos);
S3へのアップロード
本番環境ではAmazon S3やCloudflare R2などのオブジェクトストレージを使うことを推奨します。
composer require league/flysystem-aws-s3-v3
// .env
AWS_ACCESS_KEY_ID=xxx
AWS_SECRET_ACCESS_KEY=xxx
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=my-bucket
// アップロード
$path = $request->file('avatar')->store("avatars/{$user->id}", 's3');
$url = Storage::disk('s3')->url($path);
まとめ
Laravelのファイルアップロードは、FormRequestによるバリデーション・Storageファサードによる保存・URL生成という3ステップで実装できます。画像リサイズ・S3連携を加えることで本番運用に耐える実装が完成します。弊社では物件画像・プロフィール写真など、様々なファイルアップロード機能を実装してきました。
LaravelによるWebシステム開発のご相談はお気軽にどうぞ。