Table of Contents
上传文件到阿里云 OSS 可以后端传,也可以前端传,我目前碰到的公司项目都是后端传的,现在我自己的项目也用到 OSS 了,但因为服务器带宽问题,我选择了前端上传。
首先,创建 bucket,这个直接创建就可以了,然后设置 bucket 的跨域设置,否则会出现无法上传的问题。
然后,参考这篇文章《客户端签名直传》。阿里文档里有不少文章,这篇比较全面。
但我一开始参考的这篇文章:《Browser.js授权访问》,所以……中间有些过程挺莫名其妙的。这里也只能记录一下服务端和客户端的处理。
服务端
首先,安装 ali-oss 这个包,前端也要这个包。然后服务端代码如下:
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Credentials, STS } from 'ali-oss';
import { createCache } from 'cache-manager';
const cache = createCache({
// 缓存时间,毫秒
ttl: 3000 * 1000,
// 还有多长时间到期就刷新,毫秒
refreshThreshold: 3000,
});
@Injectable()
export class OssService {
constructor(private configService: ConfigService) {}
async getSTSToken() {
const cacheKey = 'sts-token';
const cached = (await cache.get(cacheKey)) as { credentials: Credentials };
if (cached) {
return {
AccessKeyId: cached.credentials.AccessKeyId,
AccessKeySecret: cached.credentials.AccessKeySecret,
SecurityToken: cached.credentials.SecurityToken,
};
}
const sts = new STS({
accessKeyId: this.configService.get('ALI_OSS_ACCESS_Key'),
accessKeySecret: this.configService.get('ALI_OSS_SECRET'),
});
// roleArn填写步骤2获取的角色ARN,例如acs:ram::175708322470****:role/ramtest。
// policy填写自定义权限策略,用于进一步限制STS临时访问凭证的权限。如果不指定Policy,则返回的STS临时访问凭证默认拥有指定角色的所有权限。
// 3000为过期时间,单位为秒。
// sessionName用于自定义角色会话名称,用来区分不同的令牌,例如填写为sessiontest。
const result = await sts.assumeRole(
'xxxxxxxx',
``,
3000,
'sessiontest',
);
cache.set(cacheKey, result);
const data = {
AccessKeyId: result.credentials.AccessKeyId,
AccessKeySecret: result.credentials.AccessKeySecret,
SecurityToken: result.credentials.SecurityToken,
};
return data;
}
}
为了避免重复请求,我这里用缓存。相关包是 cache-manager。
客户端
客户端的代码如下:
import { apiBundle } from '@/boot/axios'
import type * as OSSTypes from './types'
import OSS from 'ali-oss'
/** 上传文件到 oss */
export async function uploadToAliOss(file: File, filename: string) {
// 客户端不缓存凭证,因为客户端的时间不可靠,对比过期时间可能有问题
const { data } = await apiBundle.API<OSSTypes.STSResponseData>({
url: '/oss/sts/token',
method: 'get',
})
const client = new OSS({
secure: true,
bucket: 'xxx',
region: 'oss-cn-xxx',
accessKeyId: data.AccessKeyId,
accessKeySecret: data.AccessKeySecret,
stsToken: data.SecurityToken,
})
const result = await client.put(filename, file)
return result
}
阿里文章里是在客户端判断的过期时间,但因为客户端的时间不可控,所以我这里没有判断,反正服务端我已经做了缓存了。缺点就是每次上传前都要请求一下服务端。