几乎每个 Web 应用都会涉及文件上传,如用户头像、文章配图、附件等。Laravel 提供了强大的文件上传处理能力,结合统一的文件系统 API,让你可以轻松将文件存储到本地磁盘、云存储(如阿里云 OSS、Amazon S3)等。 本章将详细介绍如何在 Laravel 中处理文件上传并进行存储。
Laravel 的文件系统配置位于 config/filesystems.php,默认提供了 local 和 public 两个磁盘:
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
],
local 磁盘:文件存储在 storage/app 目录,默认不公开访问。public 磁盘:文件存储在 storage/app/public,通过 php artisan storage:link 创建软链接后,可在 public/storage 访问。league/flysystem-aws-s3-v3)并配置密钥。
文件上传表单必须设置 enctype="multipart/form-data",并包含 CSRF 令牌:
<form method="POST" action="{{ route('avatar.upload') }}" enctype="multipart/form-data">
@csrf
<div class="form-group">
<label for="avatar">选择头像:</label>
<input type="file" class="form-control @error('avatar') is-invalid @enderror" id="avatar" name="avatar">
@error('avatar')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">上传</button>
</form>
在控制器中获取上传文件,验证后存储:
use Illuminate\Http\Request;
public function uploadAvatar(Request $request)
{
$request->validate([
'avatar' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048', // 最大2MB
]);
if ($request->hasFile('avatar')) {
$file = $request->file('avatar');
// 存储文件,返回保存路径
$path = $file->store('avatars', 'public');
// 也可以自定义文件名
// $path = $file->storeAs('avatars', 'avatar-'.time().'.'.$file->getClientOriginalExtension(), 'public');
// 将路径保存到数据库(例如用户表)
auth()->user()->update(['avatar' => $path]);
return back()->with('success', '头像上传成功!');
}
return back()->with('error', '未选择文件');
}
$request->hasFile('avatar') — 判断文件是否存在。$request->file('avatar') — 获取 UploadedFile 对象。store($path, $disk) — 将文件存储到指定磁盘的路径下,自动生成唯一文件名。storeAs($path, $name, $disk) — 存储并指定文件名。getClientOriginalName() — 获取原始文件名。getClientOriginalExtension() — 获取原始扩展名。getSize() — 获取文件大小(字节)。Laravel 提供了丰富的文件验证规则:
file — 必须是成功上传的文件。image — 必须是图片(jpg, png, bmp, gif, svg)。mimes:foo,bar — 限制 MIME 类型,如 mimes:jpeg,png。mimetypes:text/plain,image/png — 更精确的 MIME 类型限制。max:2048 — 最大文件大小(KB)。dimensions — 限制图片尺寸,如 dimensions:min_width=100,min_height=200。示例:
$request->validate([
'avatar' => 'required|image|dimensions:min_width=100,min_height=100|max:2048',
]);
使用 Storage 门面可以更灵活地操作文件:
use Illuminate\Support\Facades\Storage;
// 存储到默认磁盘(local)
$path = Storage::putFile('avatars', $request->file('avatar'));
// 存储到 public 磁盘
$path = Storage::disk('public')->putFile('avatars', $request->file('avatar'));
// 存储并指定文件名
$path = Storage::disk('public')->putFileAs('avatars', $request->file('avatar'), 'avatar.jpg');
对于公开磁盘的文件,可以通过 asset() 辅助函数生成 URL:
<img src="{{ asset('storage/' . $user->avatar) }}" alt="头像">
如果文件存储在私有磁盘,可以通过控制器返回文件:
public function showAvatar($userId)
{
$user = User::findOrFail($userId);
$path = storage_path('app/' . $user->avatar);
return response()->file($path);
}
public 磁盘前必须执行 php artisan storage:link 创建软链接,否则无法通过 /storage 访问文件。
1. 路由定义
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::post('/profile/avatar', [ProfileController::class, 'uploadAvatar'])->name('profile.avatar');
2. 控制器方法
public function edit()
{
return view('profile.edit', ['user' => auth()->user()]);
}
public function uploadAvatar(Request $request)
{
$request->validate([
'avatar' => 'required|image|max:2048',
]);
$user = auth()->user();
$path = $request->file('avatar')->store('avatars', 'public');
// 删除旧头像(如果有)
if ($user->avatar) {
Storage::disk('public')->delete($user->avatar);
}
$user->update(['avatar' => $path]);
return redirect()->route('profile.edit')->with('success', '头像更新成功!');
}
3. 视图 profile/edit.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<h1>个人资料</h1>
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
<div class="row">
<div class="col-md-4">
<div class="card">
<div class="card-body text-center">
@if ($user->avatar)
<img src="{{ asset('storage/' . $user->avatar) }}" class="img-thumbnail mb-3" width="150">
@else
<div class="mb-3" style="width:150px;height:150px;background:#eee;display:flex;align-items:center;justify-content:center">
暂无头像
</div>
@endif
<form method="POST" action="{{ route('profile.avatar') }}" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<input type="file" class="form-control @error('avatar') is-invalid @enderror" name="avatar">
@error('avatar')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">更新头像</button>
</form>
</div>
</div>
</div>
<div class="col-md-8">
<!-- 其他资料表单 -->
</div>
</div>
</div>
@endsection
upload_max_filesize、post_max_size、memory_limit。对于超大文件,考虑使用分片上传或异步队列处理。
store() 方法会自动生成唯一文件名(基于时间+随机字符串)。也可以手动生成:$filename = time() . '_' . uniqid() . '.' . $file->getClientOriginalExtension();
league/flysystem-aws-s3-v3),配置 config/filesystems.php 中的 disks,然后使用 Storage::disk('oss')->putFile(...) 即可。
文件上传与存储是 Laravel 开发中的常见需求。通过 Request 对象获取上传文件,配合验证规则和存储方法,可以安全高效地处理用户文件。
掌握 Storage 门面的使用,能够让你轻松应对本地、云存储等多种场景。下一章我们将学习如何通过邮件通知用户,构建更完善的交互体验。