文件上传失败多因PHP或Web服务器配置不当:先检查post_max_size、upload_max_filesize及Nginx的client_max_body_size;确认表单name与request()->file()参数一致;避免中间件提前解析请求;确保storage/app目录权限正确;验证规则中慎用required,注意mimes基于文件内容检测。

文件上传时返回 500 或空响应,$_FILES 为空怎么办
多数情况不是代码写错了,而是 PHP 层面根本没收到文件。先确认 post_max_size 和 upload_max_filesize 是否足够——Laravel 不会报错提示你改 php.ini,它只是静默丢弃超限请求。
- 用
php -i | grep -E "(post_max_size|upload_max_filesize|file_uploads)"查当前值,别只看phpinfo()页面(可能和 CLI/CLI-FPM 不一致) - Nginx 用户必须加
client_max_body_size,且要大于 PHP 的post_max_size,否则请求在进 PHP 前就被截断,$_FILES必然为空 - Apache 下如果用了
mod_security,它可能拦截 multipart 请求但不返回明确错误,临时关掉模块验证是否是它在“背锅”
Laravel request()->file() 返回 null,但 $_FILES 有数据
说明 Laravel 解析失败,常见于表单字段名不匹配或中间件干扰。Laravel 不会自动把任意 $_FILES 键转成文件对象,它只认你在 request()->file('<code>field_name') 里写的那个名字。
- 检查 HTML 表单中
<input type="file" name="avatar">的name是否和代码里request()->file('avatar')完全一致(区分大小写) - 确保没在中间件里调用过
$request->all()或$request->input()—— 这会触发 Laravel 提前解析全部输入,导致后续file()调用失效(PHP 的$_FILES只能读一次) - 如果你用的是数组式上传如
name="photos[]",request()->file('photos')返回的是数组,但request()->file('photos.0')会返回 null;得用request()->file('photos')[0]
上传成功但文件没写入 storage/app,或提示 Unable to write file at path
不是 Laravel 权限不够,是它默认用的本地磁盘驱动依赖 storage/app 目录可写,而这个目录常被部署脚本忽略。
- 运行
ls -ld storage/app,确认 Web 服务器用户(如www-data或nginx)对该目录有w权限,不是只对父目录有权限 -
php artisan storage:link只创建软链,不影响写入;真正要改的是storage下各子目录的属主和权限,建议统一执行:sudo chown -R www-data:www-data storage && sudo chmod -R 755 storage - 如果用了
APP_ENV=production,某些托管环境会禁用fopen写本地路径,此时需显式配置为'driver' => 'local'并确认root指向绝对路径(相对路径在 CLI 和 Web 环境下解析结果不同)
前端传了文件,request()->validate() 却直接抛 422,没走到控制器逻辑
验证器在中间件阶段就介入了,一旦规则里写了 'avatar' => 'required|file|mimes:jpg,png',但请求里没带 avatar 字段(比如用户没选文件、或字段名拼错),就会立刻返回 422,根本不会进控制器。
- 不要在验证规则里盲目加
required,除非业务上确实不允许空上传;更安全的是用'avatar' => 'nullable|file|mimes:jpg,png' - 注意
mimes规则依赖文件内容检测(通过finfo),不是只看后缀;如果上传的是 .jpg 但实际是 PNG 内容,也会失败,调试时可用dd($request->file('avatar')->getMimeType())看真实类型 - Postman 测试时,务必选 “form-data” 模式并点 “File” 类型按钮传文件,别用 “Text” 模式填路径——那只会传字符串,
request()->file()一定为 null
路径和权限问题最常出现在部署后首次上传,因为开发机和生产机的用户、SELinux、容器挂载策略完全不同。别假设“本地能跑线上就能跑”,每次上线后第一件事就是手动 touch storage/app/test.txt 验证写权限是否真的生效。










