ThinkPHP 8.0 文件上传 OSS 整合教程【保姆级】
ThinkPHP8.0整合阿里云OSS上传采用配置驱动机制,通过安装SDK、设置环境变量、启用OSS磁盘实现统一调用。控制器上传需校验文件类型,用uniqid重命名并按日期分目录存储,防止路径穿越。可选直传签名用于大文件前端上传。
ThinkPHP 8.0 整合阿里云 OSS 上传,说到底就是“配置驱动 + 安全上传 + 统一调用”这么一套组合拳。别想着手写 SDK 实例或者把密钥硬编码在代码里,直接用框架自带的 filesystem 驱动机制,既省事又方便以后切换存储后端,这才是正经做法。

下面一步步拆开来说,从安装到上线,每个环节都给你讲清楚。
一、安装 SDK 并配置环境变量
在项目根目录执行 composer require aliyuncs/oss-sdk-php,这是前提。确保 PHP 已经装好了 curl 和 openssl 扩展——OSS 通信全靠这俩,缺一不可。
然后打开 .env 文件,把 OSS 凭证加进去。记住,凭证绝对不要写死在代码里,那是给自己挖坑。这样写:
OSS_ACCESS_ID=your-access-key-id OSS_ACCESS_KEY=your-access-key-secret OSS_BUCKET=your-bucket-name OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com OSS_CDN_DOMAIN=https://cdn.yourdomain.com
Endpoint 要填你创建 Bucket 时选的地域,比如北京就是 oss-cn-beijing.aliyuncs.com。如果开了 CDN 加速,直接把加速域名填进去,访问速度能提升不少。
二、启用并配置 OSS 文件系统磁盘
编辑 config/filesystem.php,在 'disks' 数组里追加一段 OSS 配置:
'oss' => [
'type' => 'oss',
'access_id' => env('OSS_ACCESS_ID'),
'access_key' => env('OSS_ACCESS_KEY'),
'bucket' => env('OSS_BUCKET'),
'endpoint' => env('OSS_ENDPOINT'),
'cdn_domain' => env('OSS_CDN_DOMAIN'),
'ssl' => true,
],
接下来要考虑默认磁盘用哪个。如果想让整个项目默认就走 OSS,那把 'default' 改成 'oss' 就行。如果还想用本地存储做 backup,就保留 'local',需要上传 OSS 的地方显式调用 Storage::disk('oss')->put(...)——灵活度更高。
三、控制器中安全上传文件
上传逻辑的核心就三点:校验、重命名、路径隔离。来看一个典型的示例代码,支持图片和常见文档类型:
use think\facade\Storage;
use think\Request;
public function upload(Request $request)
{
$file = $request->file('file');
if (!$file) {
return json(['code' => 400, 'msg' => '未检测到文件']);
}
// 白名单校验(扩展名 + MIME)
$ext = $file->getExtension();
$allowed = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'docx', 'xlsx'];
if (!in_array(strtolower($ext), $allowed)) {
return json(['code' => 400, 'msg' => '不支持的文件类型']);
}
// 生成唯一对象 Key:避免路径遍历,不直接用原始名
$dateDir = date('Ym/d');
$safeName = uniqid() . '.' . $ext;
$objectKey = "uploads/{$dateDir}/{$safeName}";
try {
// 上传至 OSS,并返回可访问 URL
$result = Storage::disk('oss')->put($objectKey, $file->getStream());
if ($result) {
$url = Storage::disk('oss')->url($objectKey);
return json(['code' => 200, 'msg' => '上传成功', 'data' => ['url' => $url, 'path' => $objectKey]]);
}
} catch (Exception $e) {
return json(['code' => 500, 'msg' => 'OSS 上传失败:' . $e->getMessage()]);
}
}
这里有两个需要重点说的地方:
$file->getStream()是 TP8 推荐的做法,比用fopen(..., 'rb')更符合框架的抽象层设计,别走弯路。uploads/Ym/d/这种按年月日分目录的结构,既方便按月归档文件,也避免了单目录下文件数量爆炸带来的性能问题。- 绝不拼接用户原始文件名——路径穿越攻击(比如
../../etc/passwd)就是这么来的,必须用uniqid()生成安全的新名称。 - 最后返回的
url()会自动适配 CDN 或 HTTPS 配置,不用自己操心。
四、补充:直传签名(可选,用于大文件或前端直传)
如果你的项目需要前端直接向 OSS 上传大文件(或者想减轻服务器中转压力),那需要后端生成一个签名策略。这不是常规上传的必选项,但对提升用户体验帮助很大。简单说一下流程:
- 后端生成 base64 编码的 policy,里面限制过期时间、文件大小、允许的前缀等。
- 用 AccessKeySecret 对 policy 签名,得到 signature。
- 把 AccessKeyId、policy、signature、host(Bucket.Endpoint)、callback 等参数一起返回给前端。
- 前端用
XMLHttpRequest或阿里云的aliyun-oss-browser-uploadSDK 直接提交。
这个方案需要额外配置 CORS 跨域、RAM 子账号权限以及回调服务器,适合中大型项目。小项目的话,老老实实走服务端中转更可控,也更容易排查问题。


































