阿里云对象存储OSS的前端直传-demo

原由

在项目里有时候会碰到比如上传文件相关的,一般都是后端提供个接口,然后我们上传的时候后端再传到阿里OSS或者其他服务商的对象存储,然后把最终的url拿到存起来或者返回给前端,这种方式其实在上传图片的频率不高的业务场景中可能并无大碍,但是如果你的项目是相册类的,资源提供类的,总之就是有很频繁的上传文件的场景,可能服务器的带宽就有点扛不住了,那么有没有更好的解决方案呢?服务端签名,客户端直传其实像阿里、腾讯、七牛等云服务厂商都提供的有类似阿里的STS(Security Token Service)临时访问权限管理服务,这次就以阿里云为例,给大家介绍下如何使用STS Token,来实现在服务端签名出STS token,然后提供给前端,让前端直接用这个Token向阿里云直传文件服务端签名,获取到STS token我们这里直接以Node.js为例,其他语言的服务可以在阿里云的SDK参考(STS)文档里面找到,有Python、Java...首先我们需要先装一个sts-sdk的npm包:@alicloud/sts-sdk(Nodejs version >= 8.5.0)

后端

javascript 复制代码
npm install @alicloud/sts-sdk

然后我们在utils新建一个文件oss-sts-server.js,用来生成STS Token提供给前端使用(这里只作为实例,后续大家可以自行封装)

javascript 复制代码
const StsClient = require('@alicloud/sts-sdk');

/**
 * 生成STStoken
 * @param accessKeyId AccessKey ID
 * @param accessKeySecret 从STS服务获取的临时访问密钥AccessKey Secret
 * @param roleArn 指定角色的ARN
 * @param roleSessionName 时临时Token的会话名称,自己指定用于标识你的用户,或者用于区分Token颁发给谁
 * @param durationSeconds token 有效事件,单位:秒
 * @param policy 指定的授权策略 默认为null
 * @return
 *   RequestId, 请求id
 *   AssumedRoleUser: {
 *     Arn, ${roleArn}/${roleSessionName}
 *     AssumedRoleId
 *   },
 *   Credentials: {
 *     SecurityToken, sts token
 *     AccessKeyId, accessKeyId
 *     AccessKeySecret, accessKeySecret
 *     Expiration 过期时间
 *   }
 */
export default function generateSTSToken(accessKeyId, accessKeySecret, roleArn, roleSessionName = 'external-username', durationSeconds = 3600, policy = null) {
  const sts = new StsClient({
    endpoint: 'sts.aliyuncs.com', // check this from sts console
    accessKeyId, // check this from aliyun console
    accessKeySecret // check this from aliyun console
  });
  return res = await sts.assumeRole(roleArn, roleSessionName, policy, durationSeconds);

这个generateSTSToken函数的几个入参我来解释一下,通常我们在用阿里云或者腾讯云的时候通常会开一个RAM账户也是就子账户,我们用子账户登录到阿里云后台后,到对象存储(OSS)控制台页面,找到安全令牌(子账号授权) ,也就是下图中标记的地方,点击上面的前往RAM控制台按钮

随后点击开始授权按钮,之后你就可以得到accessKeyId、accessKeySecret、roleArn、roleSessionName还有默认的过期时间DurationsSeconds,如下图所示,由于我之前授权过一次,所以会有左下角这个提示,这几个参数一定到保存好,不要泄露,一旦泄露,请更改RAM账户密码,并重新生成,使之前的失效

完善服务端提供的数据

这个时候其实已经拿到accessKeyId、accessKeySecret、stsToken、expiration这四个参数了

但是客户端还需要bucket:对象存储的命名空间和region:bucket所在地域这两个参数

这个bucket其实就是对应的使用的那个bucket,这个可以在阿里云对象存储页面看到,有一个bucket列表,就是你要是用的那个bucket的名字region就是某一个bucket所在的地域,比如我这个就是oss-cn-beijing

此时服务端的工作已经完结了,可以提供前端一个接口,通过鉴权之后,返回给前端这么几个参数,接下来,让我们把舞台交给我们的前端~

javascript 复制代码
{
  accessKeyId,
  accessKeySecret,
  stsToken,
  bucket,
  region,
  expiration
}

前端

前端er们来跟我 左边一起画个龙 在你右边 画一道彩虹(bushi)首先我们也新建一个oss-sts-client.js/ts,然后安装一个ali-sdk/ali-oss的包,对了不支持IE10和之前的IE版本啊

javascript 复制代码
npm install ali-oss --save

然后复制下面的内容到这个文件中,用js的同学可以把ts相关的代码删掉

javascript 复制代码
// 这个是服务端提供给前端的一个请求接口,返回上面我们提到的几个参数
import { getOssSTSToken } from "./request"; 
// @ts-ignore 忽略ts报错,ali-oss赶紧提供@types包吧,文档难看懂,库也没个文档,你们文档要是维护的好,我还用写这个?我都不想吐槽......(bushi)
import OSS from 'ali-oss'

type OssStsType = {
  accessKeyId: string
  accessKeySecret: string
  stsToken: string
  expiration: number // 这个是前端计算出的还有多少秒token过期
  region: string
  bucket: string
}

/**
 * 获取OSSClient
 * @param accessKeyId AccessKey ID
 * @param accessKeySecret 从STS服务获取的临时访问密钥AccessKey Secret
 * @param stsToken 从STS服务获取的安全令牌(SecurityToken)
 * @param region Bucket所在地域
 * @param bucket Bucket名称
 */
export default async function getOssClient () {
  const { code, data: params } = await getOssSTSToken();
  if (code !== 200) return false; // 如果请求出错,在上游处理
  const client = new OSS({
    ...params,
    refreshSTSTokenInterval: params.expiration,
    // 刷新临时访问凭证的时间间隔,单位为毫秒。
    //(这个refreshSTSToken是文档里的,为了保险各位可以在每次上传前先检查一次过期没有,不要依赖提供的这个方法)
    refreshSTSToken: async () => {
      const { code, data } = await getOssSTSToken(); // 过期后刷新token
      if (code === 200) {
        return data
      }
    },
  })
  return client
}

好了,到现在为止我们已经封装好了这个前端需要在上传文件的时候调用的方法了

前端维护STS Token

首先我们在前端页面第一次上传文件的时候,要调用这个getOssClient方法获取到oss-client这个对象实例,才能用这个实例进行上传操作,之后上传的时候需要先判断一下token过期了没有,如果没有过期,还是用这个实例进行上传操作,如果过期了,重新生成一个实例!这里我们就拿一个简单的上传小文件为例(大文件分片上传,和上传成功回调(需要后端同学提供回调地址) 可以自己去看文档,我就不展开细说了)

javascript 复制代码
async function uploadFileAction(file, client) {
  let newClient = client;
  // 伪代码:
  // if (!newClient || token is expired) { // 如果是没有实例对象或者token过期了就要重新生成
  //  newClient = await getOssClient(); // 调用上面我们封装好的一个方法
  // }
  const filePath = 'xxx/xxx/' // 最中在bucket中的存放的路径根据业务需要自行设置,文件名也是可以自行设置
  const { res, name, url } = await newClient.put(`${filePath}${file.name}`, file);
  if (res.status === 200) {
    // 这里拿到上传成功的文件的url
    return url
  }  
}

关于这里oss-client的维护策略,各位就仁者见仁智者见智吧,方案很多,怎么贴合业务怎么来,但是不推荐往localStorage和sessionStorage和indexDB里面存STS token等那些参数,你怎么就确定你的用户不是一名前端er呢?

CORS的问题

还没完啊,xdm 稍等一下,以上的都完了之后,我们在本地联调的时候如果没有开代理还是会有CORS的问题,这时候还是要去服务端去配置,找到跨域设置,进去创建一个规则,方法看你用什么就勾上什么,来源和允许Headers 直接给干成*就完事了

案例-分片上传

相关推荐
前端大卫6 分钟前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘21 分钟前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare22 分钟前
浅浅看一下设计模式
前端
Lee川26 分钟前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix1 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人1 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl1 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人1 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼1 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端