Skip to content

Laravel - 文件上传

Laravel 可以轻松处理用户上传的文件。这通常包括创建一个用于文件选择的 HTML 表单和一个用于处理和存储上传文件的控制器动作(action)。

要允许文件上传,你的 HTML 表单的 enctype 属性必须设置为 multipart/form-data。

视图示例(resources/views/upload.blade.php):

@extends('layouts.app') {{-- 假设有一个布局文件 --}}
@section('content')
<div class="container">
<h2>Upload a File</h2>
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
@if (session('filePath'))
<br>Uploaded file path: <a href="{{ session('filePath') }}" target="_blank">{{ session('filePath') }}</a>
@endif
</div>
@endif
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('file.upload.post') }}" method="POST" enctype="multipart/form-data">
@csrf {{-- CSRF 保护令牌 --}}
<div class="mb-3">
<label for="fileInput" class="form-label">Select file:</label>
<input type="file" class="form-control" id="fileInput" name="user_file" required>
</div>
<button type="submit" class="btn btn-primary">Upload File</button>
</form>
</div>
@endsection

表单中的关键元素:

  • method="POST":文件上传应使用 POST 方法。
  • enctype="multipart/form-data":文件上传必不可少。
  • @csrf:Laravel 的 CSRF 保护指令。
  • <input type="file" name="user_file">:文件输入字段。name 属性(此处为 user_file)用于在控制器中访问文件。

创建一个控制器来处理文件上传请求。我们将其命名为 FileUploadController。

php artisan make:controller FileUploadController

在 app/Http/Controllers/FileUploadController.php 中:

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
use Illuminate\Support\Facades\Storage; // 对于文件存储很重要
class FileUploadController extends Controller
{
/**
* Display the file upload form.
*/
public function showUploadForm(): View
{
return view('upload');
}
/**
* Handle the uploaded file.
*/
public function storeUpload(Request $request): RedirectResponse
{
// 1. 验证上传的文件
$request->validate([
'user_file' => 'required|file|mimes:jpg,jpeg,png,pdf,doc,docx|max:2048', // 验证规则示例
]);
// 2. 检查是否有文件上传
if ($request->hasFile('user_file')) {
$file = $request->file('user_file');
// 3. 获取文件信息 (可选,用于显示或记录日志)
$originalName = $file->getClientOriginalName();
$extension = $file->getClientOriginalExtension();
$size = $file->getSize(); // 大小,单位字节
$mimeType = $file->getMimeType();
// 4. 存储文件
// 选项 A: 存储到 'public' 存储盘(通常是 storage/app/public)
// `store` 方法会生成一个唯一的文件名以避免冲突。
// 确保运行 `php artisan storage:link` 使这些文件可以通过 Web 访问。
$path = $file->store('uploads', 'public'); // 存储到 'storage/app/public/uploads'
$publicUrl = Storage::disk('public')->url($path);
// 选项 B: 使用原始名称存储 (注意名称冲突)
// $filename = time() . '_' . $originalName;
// $path = $file->storeAs('uploads', $filename, 'public');
// $publicUrl = Storage::disk('public')->url($path);
// 选项 C: 直接移动到 public 目录 (使用 Storage Facade 较少见)
// $destinationPath = public_path('/user_uploads');
// $safeName = time() . '_' . $file->getClientOriginalName();
// $file->move($destinationPath, $safeName);
// $publicUrl = asset('user_uploads/' . $safeName);
// 演示目的,我们将返回一些信息
return back()
->with('success', 'File uploaded successfully!')
->with('fileName', $originalName)
->with('filePath', $publicUrl) // 传递可访问的 URL
->with('fileSize', round($size / 1024, 2) . ' KB')
->with('fileMimeType', $mimeType);
} else {
return back()->withErrors(['user_file' => 'No file was uploaded.']);
}
}
}

控制器逻辑说明:

  • 验证: 对于安全性和应用程序稳定性至关重要。使用 Laravel 的验证规则(file、mimes、max 大小以千字节为单位)。
  • $request->hasFile('user_file'): 检查请求中是否存在指定名称的文件。
  • $request->file('user_file'): 获取 Illuminate\Http\UploadedFile 的实例。
  • 文件信息: getClientOriginalName()、getClientOriginalExtension()、getSize()、getMimeType() 等方法提供有关上传文件的详细信息。
  • 存储文件: - UploadedFile 对象上的 store('directory', 'disk') 方法是推荐的方式。它处理唯一命名,并使用 Laravel 的文件系统配置(config/filesystems.php)。 - 'public' 存储盘通常映射到 storage/app/public。你必须运行 php artisan storage:link 来创建从 public/storage 到 storage/app/public 的符号链接,从而使这些文件可以通过 Web 访问,通过 asset('storage/path_to_file') 或 Storage::url('path_to_file') 进行访问。
  • 反馈: 重定向回带有成功消息,可选地包含文件详细信息。

在 routes/web.php 中定义用于显示表单和处理 POST 请求的路由:

use App\Http\Controllers\FileUploadController;
// 用于显示上传表单的路由
Route::get('/upload-file', [FileUploadController::class, 'showUploadForm'])->name('file.upload.form');
// 用于处理文件上传 POST 请求的路由
Route::post('/upload-file', [FileUploadController::class, 'storeUpload'])->name('file.upload.post');
  1. 如果尚未运行,请运行 php artisan storage:link(每个项目只需运行一次)。

  2. 在浏览器中导航到 http://your-app.test/upload-file。

  3. 使用表单选择一个文件并点击“上传文件”。

  4. 你应该会看到一个成功消息,并且文件应该存储在 storage/app/public/uploads(或你指定的目录)中。成功消息也应该显示访问该文件的公共 URL。

实际应用:用户头像或文档管理

文件上传对于以下功能至关重要:

  • 用户个人资料图片(头像)。
  • 将文档附加到记录(例如,发票、报告)。
  • 内容管理系统,用户上传图片或其他媒体。

常见的学习障碍:文件权限和 storage:link

一个常见的困惑点是文件的可见性。存储在 storage/app 中的文件不能直接通过 Web 访问。对于需要公开的文件(如网站上显示的图片),请将它们存储在 'public' 存储盘上,并确保你已运行 php artisan storage:link。此外,确保你的 Web 服务器对 storage/app 目录及其子目录具有写入权限。