0

File uploads — $request->file(), store(), storeAs()

Intermediate5 min read·lv-09-004
security

Concept

File uploads in Laravel are handled through $request->file() which returns Illuminate\Http\UploadedFile instances (extending Symfony's UploadedFile). This object wraps PHP's $_FILES data with a rich OOP API for validation and storage.

$request->file(string $key): Returns an UploadedFile or null. For multiple files (input name="photos[]"), returns an array.

$request->hasFile(string $key): True if the key contains an uploaded file (not just a form field).

UploadedFile methods:

  • ->isValid(): True if upload succeeded without errors.
  • ->getClientOriginalName(): Original filename from the browser — NEVER use this as the stored filename without sanitization.
  • ->getClientOriginalExtension(): Client-reported extension — NEVER trust this for MIME type.
  • ->getMimeType(): Detected MIME type from file content (uses finfo).
  • ->getSize(): File size in bytes.
  • ->getError(): Upload error code (0 = success).

->store(string $path, string $disk = 'default'): Moves the file to storage using Laravel's filesystem. Generates a unique filename. Returns the stored path. Equivalent to Storage::put().

->storeAs(string $path, string $name, string $disk = 'default'): Store with a specific filename.

->storePublicly(string $path): Store in a publicly accessible location.

Code Example

php
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;

class AvatarController extends Controller
{
    public function upload(Request $request): JsonResponse
    {
        $request->validate([
            'avatar' => [
                'required',
                'file',
                'image',           // validates as image (MIME check)
                'mimes:jpg,jpeg,png,webp',
                'max:2048',        // max 2MB (in kilobytes for max rule)
                'dimensions:min_width=100,min_height=100,max_width=2000',
            ],
        ]);

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

        // Validate upload success
        if (!$file->isValid()) {
            return response()->json(['error' => 'Upload failed'], 422);
        }

        // Store on S3 (or configured disk) with auto-generated unique name
        $path = $file->store('avatars', 's3'); // "avatars/generated-uuid.jpg"

        // Or store with a specific name
        $userId = auth()->id();
        $ext = $file->getClientOriginalExtension(); // "jpg" — use as extension only
        $path = $file->storeAs('avatars', "$userId.$ext", 's3');

        auth()->user()->update(['avatar_path' => $path]);

        return response()->json([
            'path' => $path,
            'url'  => \Illuminate\Support\Facades\Storage::disk('s3')->url($path),
        ]);
    }

    public function multiple(Request $request): JsonResponse
    {
        $request->validate([
            'photos'   => 'required|array|max:5',
            'photos.*' => 'image|max:5120',
        ]);

        $paths = [];
        foreach ($request->file('photos') as $photo) {
            $paths[] = $photo->store('photos', 'public');
        }

        return response()->json(['paths' => $paths]);
    }
}