Skip to content

Laravel File Upload Implementation Guide | Validation, Storage, and S3 Support

Category: Laravel / PHP

Laravel has rich file upload features, allowing you to implement validation, storage abstraction, and S3 integration with a consistent API. This article systematically explains practical file upload implementation, starting from how to use Request::file(), through validation rules and saving with the Storage facade, to S3 driver configuration.

Client Request file() / validate() Storage disk()->put() storage/app/public S3 Bucket
Diagram: Laravel upload flow (Request → Storage → local/S3)

How to use Request::file() and hasFile()

To receive an uploaded file in a Laravel controller, use the $request->file() method. This method returns an Illuminate\Http\UploadedFile instance, which extends Symfony's UploadedFile.

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

class FileUploadController extends Controller
{
    public function store(Request $request)
    {
        // ファイルが送信されているか確認
        if (!$request->hasFile('avatar')) {
            return response()->json(['error' => 'ファイルが選択されていません。'], 422);
        }

        // UploadedFile インスタンスを取得
        $file = $request->file('avatar');

        // アップロードが成功しているか確認
        if (!$file->isValid()) {
            return response()->json(['error' => 'ファイルのアップロードに失敗しました。'], 422);
        }

        // ファイル情報の取得
        $originalName  = $file->getClientOriginalName();  // 元のファイル名
        $extension     = $file->getClientOriginalExtension(); // 元の拡張子
        $mimeType      = $file->getMimeType();             // サーバー側で判定したMIMEタイプ
        $clientMime    = $file->getClientMimeType();       // クライアントが申告したMIMEタイプ(信頼しない)
        $size          = $file->getSize();                 // バイト単位のサイズ
        $tmpPath       = $file->getRealPath();             // 一時ファイルのパス

        // 複数ファイルの場合(input[type="file" multiple])
        $files = $request->file('documents');
        foreach ($files as $uploadedFile) {
            // 各ファイルを処理
        }
    }
}

File validation with $request->validate()

Using Laravel's validation feature, you can declaratively validate file MIME types, sizes, image dimensions, and more. When validation fails, a 422 response is automatically returned.

public function store(Request $request)
{
    $validated = $request->validate([
        // 基本的なファイルバリデーション
        'avatar' => [
            'required',
            'file',                          // ファイルであること
            'mimes:jpg,jpeg,png,gif,webp',   // 許可するMIMEタイプ(拡張子指定でMIMEを自動判定)
            'max:5120',                      // KB単位のサイズ上限(5120KB = 5MB)
        ],

        // 画像専用のバリデーションルール
        'thumbnail' => [
            'required',
            'image',                         // 画像ファイルであること(JPEG/PNG/GIF/WebP/SVG)
            'mimes:jpg,jpeg,png,webp',
            'max:2048',                      // 2MBまで
            'dimensions:min_width=100,min_height=100,max_width=4096,max_height=4096',
        ],

        // PDFのバリデーション
        'document' => [
            'required',
            'file',
            'mimes:pdf',
            'max:20480',                     // 20MBまで
        ],

        // 複数ファイル
        'attachments'   => 'required|array|max:5',
        'attachments.*' => 'file|mimes:jpg,jpeg,png,pdf|max:10240',
    ]);

    // バリデーション済みファイルを処理
    $file = $request->file('avatar');
    // ...
}
Validation Rule Description Example
file Must be an uploaded file 'file'
image Image file (JPEG/PNG/GIF/WebP/SVG) 'image'
mimes Allow MIME types for specified extensions 'mimes:jpg,png,pdf'
mimetypes Specify MIME Type Directly 'mimetypes:image/jpeg,image/png'
max Size Limit in KB 'max:10240'(10MB)
min Minimum Size in KB 'min:1' (1 KB or larger)
dimensions Image dimensions in pixels 'dimensions:min_width=100'

Saving with Storage::disk()->put() (public / s3 disk)

Laravel's Storage facade abstracts the filesystem, allowing you to interact with local disks, public disks, and cloud storage such as S3 using the same API.

use Illuminate\Support\Facades\Storage;

public function store(Request $request)
{
    $request->validate([
        'file' => 'required|file|max:10240',
    ]);

    $file = $request->file('file');

    // ===== ローカル保存(storage/app/ 以下)=====

    // 自動でユニークなファイル名を生成して保存
    $path = $file->store('uploads');
    // → storage/app/uploads/xxxx.jpg のような形で保存される

    // 第2引数でディスクを指定
    $path = $file->store('uploads', 'local');

    // ファイル名を指定して保存
    $filename = uniqid('file_') . '.' . $file->getClientOriginalExtension();
    $path = $file->storeAs('uploads', $filename);

    // ===== public ディスク(公開アクセス可能)=====

    // storage/app/public/ 以下に保存(公開URL: /storage/... でアクセス可能)
    $path = $file->store('avatars', 'public');
    // または
    $path = Storage::disk('public')->put('avatars', $file);

    // 保存パスから公開URLを取得
    $url = Storage::disk('public')->url($path);
    // → /storage/avatars/xxxx.jpg

    // ===== Storage ファサードの各種操作 =====

    // ファイルの存在確認
    if (Storage::exists('uploads/file.jpg')) { ... }

    // ファイルの削除
    Storage::delete('uploads/file.jpg');

    // ファイルの移動
    Storage::move('uploads/temp.jpg', 'uploads/final.jpg');

    // ファイルの取得
    $contents = Storage::get('uploads/file.txt');

    // ファイルのURL取得
    $url = Storage::url('uploads/file.jpg');

    return response()->json(['path' => $path, 'url' => $url]);
}

Explanation of php artisan storage:link

To expose the storage/app/public directory to the web, you need to create a symbolic link called public/storage. The command to create this link is php artisan storage:link.

# シンボリックリンクを作成
php artisan storage:link

# 出力例:
# The [public/storage] link has been connected to [storage/app/public].

# 作成されたリンクを確認
ls -la public/storage
# lrwxrwxrwx ... public/storage -> /var/www/storage/app/public

# Docker/本番環境での注意点:
# デプロイ時に毎回実行するか、Dockerfile に含める
# RUN php artisan storage:link

Once the symbolic link is created, the file saved in storage/app/public/avatars/photo.jpg becomes accessible at https://example.com/storage/avatars/photo.jpg.

S3 driver configuration (.env)

To save files to AWS S3, the league/flysystem-aws-s3-v3 package is required (it may be included by default in Laravel 9 and later).

composer require league/flysystem-aws-s3-v3
# .env の設定
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=my-app-bucket
AWS_USE_PATH_STYLE_ENDPOINT=false

# CloudFront 経由でURLを取得したい場合
AWS_URL=https://d1234abcd.cloudfront.net
// S3 への保存
$path = $file->store('uploads', 's3');

// S3 への保存(アクセス制御を指定)
$path = Storage::disk('s3')->put('uploads/' . $filename, $file, 'public');

// 署名付きURL(一時的なプライベートファイルへのアクセス)
$url = Storage::disk('s3')->temporaryUrl(
    $path,
    now()->addMinutes(30) // 30分間有効
);

// S3 のURL取得(バケットが public の場合)
$url = Storage::disk('s3')->url($path);

Explanation of config/filesystems.php

Disk configuration is managed in config/filesystems.php. You can also add multiple S3 buckets or custom disks.

// config/filesystems.php
return [
    // デフォルトのファイルシステムディスク
    'default' => env('FILESYSTEM_DISK', 'local'),

    'disks' => [
        'local' => [
            'driver' => 'local',
            'root'   => storage_path('app'),
            'throw'  => false,
        ],

        'public' => [
            'driver'     => 'local',
            'root'       => storage_path('app/public'),
            'url'        => env('APP_URL') . '/storage',
            'visibility' => 'public',
            'throw'      => false,
        ],

        's3' => [
            'driver'                  => 's3',
            'key'                     => env('AWS_ACCESS_KEY_ID'),
            'secret'                  => env('AWS_SECRET_ACCESS_KEY'),
            'region'                  => env('AWS_DEFAULT_REGION'),
            'bucket'                  => env('AWS_BUCKET'),
            'url'                     => env('AWS_URL'),
            'endpoint'                => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
            'throw'                   => false,
        ],

        // カスタムディスク(別のS3バケットなど)
        's3-thumbnails' => [
            'driver' => 's3',
            'key'    => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_THUMBNAILS_BUCKET'),
        ],
    ],

    // storage:link コマンドで作成するリンクのマッピング
    'links' => [
        public_path('storage') => storage_path('app/public'),
    ],
];

Temporary file handling and security

Uploaded files are temporarily stored in directories such as /tmp. After processing, they are moved to their final location using move_uploaded_file() (for PHP) or Laravel's store() family of methods. Temporary files are automatically deleted when the script exits.

  • Files saved to the Storage public disk are accessible from the web, so sensitive files should not be placed there
  • It is safer to generate a random hash name using the hashName() method for file names
  • Store the original file name in the database and keep it separate from the actual saved name
  • S3 files are private by default, so for publicly accessible files, explicitly set public or use presigned URLs.
// セキュアなファイル保存の例
public function store(Request $request)
{
    $request->validate([
        'document' => 'required|file|mimes:pdf,docx|max:20480',
    ]);

    $file = $request->file('document');

    // hashName() でランダムなファイル名を生成(元の拡張子を保持)
    $hashedName = $file->hashName(); // 例: 5af75b.pdf

    // S3 の private エリアに保存
    $path = Storage::disk('s3')->putFileAs(
        'documents/' . auth()->id(),
        $file,
        $hashedName
    );

    // DBに元のファイル名と保存パスを記録
    Document::create([
        'user_id'       => auth()->id(),
        'original_name' => $file->getClientOriginalName(),
        'stored_path'   => $path,
        'mime_type'     => $file->getMimeType(),
        'size'          => $file->getSize(),
    ]);

    return response()->json(['message' => 'アップロード完了']);
}

Test files for this article (free)

❓ Frequently Asked Questions

Which validation rules apply to uploaded files in Laravel?
The validate method accepts rules such as file, mimes (allowed extensions) and max (the size ceiling in kilobytes).
What is the difference between Storage::put and store in Laravel?
store is a method on UploadedFile and generates a unique filename for you. Storage::put saves to whatever path and filename you specify.
How do I upload files to S3 from Laravel?
Put the AWS credentials in .env, configure the s3 disk in filesystems.php, then save with Storage::disk('s3')->put().

Frequently Asked Questions

What are the Validation Rules for Uploaded Files in Laravel?

The validate method allows you to specify rules such as file, mimes (allowed extensions), and max (maximum size in KB).

What is the Difference Between Storage::put and store in Laravel?

store is an UploadedFile method that automatically generates a unique filename. Storage::put saves the file with an arbitrary path and filename of your choice.

How to Upload Files to S3 in Laravel?

Set AWS credentials in .env, configure the s3 disk in filesystems.php, then save using Storage::disk('s3')->put().