Skip to content

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

Category: Laravel / PHP
This article is currently available in Japanese only. We are working on translations.

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 <code>Request::file()</code>, 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 <code>$request->file()</code> method. This method returns an <code>Illuminate\Http\UploadedFile</code> instance, which extends Symfony's <code>UploadedFile</code>.

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 <code>$request->validate()</code>

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 <code>'min:1'</code> (1 KB or larger)
dimensions Image dimensions in pixels 'dimensions:min_width=100'

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

Laravel's <code>Storage</code> 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 <code>storage/app/public</code> directory to the web, you need to create a symbolic link called <code>public/storage</code>. The command to create this link is <code>php artisan storage:link</code>.

# シンボリックリンクを作成
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 <code>storage/app/public/avatars/photo.jpg</code> becomes accessible at <code>https://example.com/storage/avatars/photo.jpg</code>.

S3 driver configuration (.env)

To save files to AWS S3, the <code>league/flysystem-aws-s3-v3</code> 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 <code>config/filesystems.php</code>. 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 <code>/tmp</code>. After processing, they are moved to their final location using <code>move_uploaded_file()</code> (for PHP) or Laravel's <code>store()</code> family of methods. Temporary files are automatically deleted when the script exits.

  • Files saved to the Storage <code>public</code> 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 <code>hashName()</code> 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 <code>public</code> 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)

  • → <a href="/ja/files/images/" class="text-primary-600 dark:text-primary-400 hover:underline">Test Images List</a> — For testing validation (mimes/dimensions)
  • → <a href="/ja/files/pdf/" class="text-primary-600 dark:text-primary-400 hover:underline">PDF Test File List</a> — for validating PDF uploads
  • → <a href="/ja/files/threshold/" class="text-primary-600 dark:text-primary-400 hover:underline">Boundary Value Test Files</a> — For checking max validation rule limits

Related articles

  • → <a href="/ja/blog/php-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">How to Implement File Upload in PHP | Complete Guide to Validation, Storage, and Security</a>
  • → <a href="/ja/blog/s3-upload-limit/" class="text-primary-600 dark:text-primary-400 hover:underline">Summary of File Upload Limits for AWS S3 and CloudFront</a>
  • → <a href="/ja/blog/file-validation-checklist/" class="text-primary-600 dark:text-primary-400 hover:underline">Web Form File Validation Implementation Checklist</a>

Frequently Asked Questions

What are the Validation Rules for Uploaded Files in Laravel?

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

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

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

How to Upload Files to S3 in Laravel?

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