必须先用 $request->hasfile() 检查文件上传,再调用 store();store() 依赖正确配置 filesystems.php 和 storage:link;后端需用 getimagesize() 增强校验,nginx/apache 必须配置 alias 才能访问 storage::url() 生成的路径。

上传前必须验证 request()->hasFile()
没做这步检查就直接调用 store(),会抛出 Call to a member function store() on null。Laravel 的 $request->file('avatar') 在表单没传文件或字段名不对时返回 null,不是空实例。
- 永远先写
if (!$request->hasFile('photo')) { return back()->withErrors(['photo' => '请上传图片']); } - 别依赖
required|image规则代替运行时检查——前端绕过、curl 测试、API 调用都可能跳过表单验证 -
hasFile()比file()更安全,后者在无文件时返回null,调用getClientOriginalName()会报错
用 store() 而不是 move(),但得配好 filesystems.php
store() 自动处理路径生成、唯一文件名、磁盘写入,而手写 move() 容易漏掉扩展名校验、覆盖风险、权限问题。但前提是 config/filesystems.php 里 'disks' => ['public' => [...]] 配置正确,且 public/storage 已执行 php artisan storage:link。
- 推荐写法:
$path = $request->file('avatar')->store('avatars', 'public');,返回类似avatars/7Y2z9X1q.jpg - 如果改用
storeAs('avatars', 'user_'.$id.'.jpg'),注意第二个参数不含路径,不能写成avatars/name.jpg - 别把
'local'磁盘当默认项用——它不支持 URL 生成,Storage::url()会报错
图片尺寸和类型校验不能只靠前端,后端要用 validate() + getimagesize()
仅靠 mimes:jpeg,png,gif 无法防伪造文件头,用户改后缀就能绕过。真实项目中见过 .php 文件重命名为 .jpg 后上传成功,再通过 URL 直接访问执行代码。
- 基础规则:
'photo' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048'(单位 KB) - 增强校验:上传后立刻用
getimagesize($request->file('photo')->getRealPath())确认是真实图像,返回false就删文件并报错 - 注意
max:2048是上传文件大小限制,不是内存限制;PHP 的upload_max_filesize和post_max_size也要同步调大
Storage::url() 返回的路径要配合 Nginx/Apache 配置才能访问
本地开发用 php artisan serve 可以直接访问 /storage/xxx.jpg,但上线后 Apache/Nginx 默认不代理 /storage 到 storage/app/public,结果浏览器 404。
- Laravel 官方要求:运行
php artisan storage:link创建软链,让public/storage指向storage/app/public - Nginx 需加 location 块:
location /storage { alias /path/to/laravel/storage/app/public; },否则Storage::url('avatars/1.jpg')生成的/storage/avatars/1.jpg找不到 - 别在 Blade 里拼接
url('/storage/'.$path)——万一哪天换磁盘策略,所有模板全挂
上传逻辑看着简单,真正卡住人的往往是磁盘配置没生效、Nginx 没配 alias、或者以为 validate 规则能拦住所有恶意文件。每一步都要单独验证输出,而不是连着跑完再看页面有没有图。










