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 }
阿里文章里是在客户端判断的过期时间,但因为客户端的时间不可控,所以我这里没有判断,反正服务端我已经做了缓存了。缺点就是每次上传前都要请求一下服务端。