Filesystem::disk('public')->putFile() 是 ThinkPHP 6.0 最推荐的上传方式,它自动校验、重命名、写入并返回路径,比 $file->move() 更安全解耦,支持远程存储切换、真实 MIME 校验、跨平台路径处理及并发安全命名。

Filesystem::disk('public')->putFile() 是 ThinkPHP 6.0 处理上传文件最推荐的方式,它自动校验、重命名、写入并返回路径,比直接调用 $file->move() 更安全、更解耦。
为什么不用 $file->move()?
虽然 $file->move() 看起来直观,但它绕过了 think-filesystem 抽象层,导致几个实际问题:
- 无法无缝切换到 OSS/COS/FTP 等远程存储(改配置就生效)
- 不自动校验 MIME 类型真实性(仅靠后缀判断易被绕过)
- 手动拼接路径容易出错,比如 Windows 反斜杠
导致 URL 访问失败 - 没有内置的并发安全命名策略(如
md5或uniqid规则)
putFile() 的参数和验证怎么配?
putFile() 本身不处理验证,验证需前置调用 validate() 方法链式调用。常见组合如下:
- 限制大小 + 后缀:
->validate(['size' => 2097152, 'ext' => 'jpg,png,gif,pem']) - 限制 MIME 类型(更可靠):
->validate(['mime' => 'image/jpeg,image/png,image/gif,text/plain']) - 同时校验大小和 MIME:
->validate(['size' => 5242880, 'mime' => 'application/pdf']) - 注意:
size单位是字节,mime必须与$_FILES['xxx']['type']实际值一致,不是浏览器伪造的后缀
上传后如何生成可访问的 URL?
上传成功返回的是相对路径(如 avatars/72/ef580909368d824e899f77c7c98388.jpg),要拼成完整 URL,必须用 Filesystem::getDiskConfig('public', 'url') 获取前缀:
$savename = Filesystem::disk('public')->putFile('avatars', $file);
$url = Filesystem::getDiskConfig('public', 'url') . '/' . str_replace('\', '/', $savename);
// 结果类似:/storage/avatars/72/ef580909368d824e899f77c7c98388.jpg
别忘了在部署后执行 php think storage:link —— 这条命令会创建 public/storage 指向 storage/app/public 的软链接(Laravel 风格),否则 Nginx/Apache 无法通过 /storage/ 路径直接访问文件。
多文件上传怎么写?
前端用 name="files[]",后端不能直接传给 putFile(),得遍历处理:
$files = request()->file('files');
$result = [];
foreach ($files as $file) {
try {
$savename = Filesystem::disk('public')->validate([
'size' => 2097152,
'ext' => 'jpg,png,gif'
])->putFile('uploads', $file);
$result[] = [
'url' => Filesystem::getDiskConfig('public', 'url') . '/' . str_replace('\', '/', $savename)
];
} catch (ValidateException $e) {
// 单个失败不影响整体,按需记录或跳过
}
}
关键点:每个 $file 是独立对象,必须单独 validate + putFile;异常捕获粒度要控制在循环内,避免一个坏文件导致整批失败。
上传逻辑看似简单,但真正上线时最容易翻车的是三件事:没跑 storage:link 导致 URL 404、验证只校后缀不校 MIME 被上传恶意脚本、多文件场景下没做单文件级异常隔离。这些都不是框架 bug,而是配置和调用姿势问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











