直接用thans/thinkphp-filesystem-cloud扩展最省事合规;手写OSS驱动易因临时文件被删、内存溢出、并发失败出错;正确做法是用UploadFile对象的getStream()获取资源句柄流式上传。

直接用 thans/thinkphp-filesystem-cloud 扩展是最省事、最符合 ThinkPHP6 文件系统规范的方案;自己手写 OSS 驱动或硬塞 OssClient::uploadFile() 容易卡在临时文件被删、内存溢出、并发失败这几处,不推荐。
为什么不能直接用 uploadFile($bucket, $key, $tmpPath)
因为 $tmpPath 指向的是 PHP-FPM 临时目录里的一个文件路径,不是内存流——上传一结束,这个文件大概率已被 PHP 自动清理(尤其在高并发或短超时配置下)。你看到的「文件不存在」或「Empty file」错误,90% 是这里崩的。
常见错误现象:
Warning: fopen(/tmp/phpXXXXXX): failed to open stream: No such file or directory-
OSSException: The specified key does not exist.(其实是本地文件已消失) - 上传大文件时内存爆掉,
Allowed memory size exhausted
正确做法是用资源句柄流式读取:fopen($file->getTmpName(), 'rb'),再传给 SDK 的 putObject()(它支持 resource 类型)。
立即学习“PHP免费学习笔记(深入)”;
thans/thinkphp-filesystem-cloud 配置要点
这个扩展基于 ThinkPHP6 的 think-file 和 Flysystem 封装,自动处理流式上传、断点续传、权限控制,关键是它绕开了 moveTo() 落盘逻辑。
配置时注意以下几项必须显式指定,缺一不可:
-
type必须设为'aliyun'(不是'oss'或'aliyuncs') -
endpoint必须带协议,如'https://oss-cn-hangzhou.aliyuncs.com'(不能只写域名) -
url是对外访问地址,用于生成公开 URL,**结尾不能加/**,否则拼接 key 时会多出双斜杠 -
bucket名称要和 OSS 控制台里完全一致(区分大小写) - RAM 子账号权限需至少包含
AliyunOSSFullAccess或自定义策略中允许oss:PutObject
示例片段(config/filesystem.php):
'aliyun' => [
'type' => 'aliyun',
'accessId' => env('OSS_ACCESS_ID'),
'accessSecret' => env('OSS_ACCESS_SECRET'),
'bucket' => env('OSS_BUCKET'),
'endpoint' => env('OSS_ENDPOINT'), // https://oss-cn-beijing.aliyuncs.com
'url' => env('OSS_URL'), // http://your-bucket.oss-cn-beijing.aliyuncs.com
],
上传代码怎么写才不踩坑
别碰 $_FILES 或 request()->file() 返回的原始数组;要用 ThinkPHP6 的 UploadFile 对象配合 Filesystem 实例。
实操建议:
- 用
think\facade\Filesystem获取驱动:Filesystem::disk('aliyun') - 上传时用
$disk->put($key, $uploadFile->getStream())—— 这个getStream()返回的就是可复用的资源句柄 - 不要手动
fclose(),框架会自动释放 - 若需重命名,
$key由你控制,比如'uploads/' . date('Ymd') . '/' . uniqid() . '.' . $uploadFile->extension() - 上传后返回的 URL 是
$disk->url($key),不是拼接出来的
最小可用示例:
use think\facade\Filesystem;
public function upload()
{
$file = request()->file('image');
if (!$file) return json(['code' => 400, 'msg' => 'no file']);
$key = 'uploads/' . date('Ymd') . '/' . uniqid() . '.' . $file->extension();
$disk = Filesystem::disk('aliyun');
try {
$disk->put($key, $file->getStream());
return json(['code' => 200, 'url' => $disk->url($key)]);
} catch (\Exception $e) {
return json(['code' => 500, 'msg' => $e->getMessage()]);
}
}
CORS 和 Bucket 权限容易被忽略
前端直传(如 Web 端表单上传)失败,80% 不是代码问题,而是 OSS 控制台没配对。
必须检查两项:
- Bucket 的 CORS 设置里,
AllowedOrigin要填你前端域名(如https://admin.example.com),不能只写*(尤其当带 Cookie 时) - 如果用子账号,RAM 策略必须显式授权
oss:PutObject和oss:GetObject,仅AliyunOSSReadOnlyAccess不够
另外,endpoint 和 region 必须匹配(比如 oss-cn-shanghai 对应的 endpoint 是 https://oss-cn-shanghai.aliyuncs.com),写错会导致签名失败,报 InvalidAccessKeyId 或 SignatureDoesNotMatch。



















