thinkphp6文件上传必须用filesystem抽象层+真实mime校验,禁用move();多文件需调用request()->file()无参获取数组,validate只支持单文件对象;putfile()自动哈希分片,putfileas()可控路径;windows需统一斜杠,finfo扩展必须启用。

ThinkPHP 6 的文件上传不能靠 move() 硬搬,必须走 Filesystem 抽象层 + 真实 MIME 校验,否则上传 shell.php 改名为 1.jpg 就能直接 RCE。
为什么 request()->file('images') 拿不到多图?
浏览器提交 <input type="file" name="images[]" multiple> 后,request()->file('images') 只返回第一个文件或 null,根本不是数组——这是 ThinkPHP 的设计限制,不是你代码写错了。
- 正确做法是调用
request()->file()(不带参数),拿到全部原始文件数组,再用array_filter()提取键名含images的项,比如images[0]、images[1] - 或者前端改用固定命名,如
images_0、images_1,后端循环读取request()->file('images_'.$i) - 别对整个数组调
validate():ThinkPHP 的验证规则只认单个File对象,传数组会跳过校验或报错
putFile() 和 putFileAs() 怎么选?
putFile() 自动哈希分片 + 重命名,适合头像、封面等无需路径可预测的场景;putFileAs() 显式控制子目录和文件名,适合商品图集、需按业务逻辑归档的场景。
-
putFile('goods', $file)→ 返回类似goods/2a/9f8e7d6c5b4a3.jpg,路径不可控但防冲突强 -
putFileAs('goods/' . date('Ym'), $file, 'goods_' . time() . '_' . uniqid() . '.jpg')→ 路径确定,方便后续批量清理或 CDN 预热 - 两者都默认校验真实 MIME 类型,但
putFileAs()不自动加日期子目录,得自己拼 - Windows 下返回路径含反斜杠
,必须用str_replace('\', '/', $savename)统一,否则 Nginx 找不到文件
validate() 里 size 和 ext 到底怎么写才安全?
仅校验后缀(ext)等于把门锁换成贴纸——攻击者用 finfo 伪造 MIME 或直接 hex 编辑就能绕过。必须配合真实类型检测。
-
validate(['size' => 2097152, 'mime' => 'image/jpeg,image/png,image/gif'])才有效,mime是关键字段,ext只能当辅助 - 框架默认启用
finfo检测,但服务器没装该扩展时会 fallback 到后缀,务必检查php -m | grep fileinfo - 不要写
'ext' => 'jpg,png,gif'单独使用,它不校验内容,只看文件名最后三个字母 - 上传失败抛
ValidateException,必须用try/catch捕获,不能只靠if判断返回值
缩略图和水印必须用 think-image 扩展
ThinkPHP 6 自带的 Filesystem 不处理图片内容,裁剪、压缩、加水印全得靠 topthink/think-image,且必须在上传成功后单独操作。
- 先
putFile()存原图,再用Image::open($full_path)->thumb(300, 300)->save($thumb_path) - 水印路径必须是绝对物理路径,
public/目录下的相对路径会失败,例如ROOT_PATH . 'public' . DS . 'static' . DS . 'water.png' - 高安全场景(如用户头像)建议原图存非 Web 目录(如
storage/app/private/),缩略图才放public,并通过 Nginxalias映射 URL,避免直接暴露存储结构 - 别在上传过程中同步生成缩略图——大图上传未完成时读取会失败,必须等
putFile()返回成功路径后再操作
最易被忽略的是 Windows 路径斜杠和 finfo 模块缺失——前者导致线上 404,后者让 mime 校验形同虚设,这两处不验证,其他都白做。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











