一文让你彻底会用对象存储OSS的前端直传,不懂就再看一遍!(bushi)

istockphoto-1272981632-170667a.jpg

原由

在项目里有时候会碰到比如上传文件相关的,一般都是后端提供个接口,然后我们上传的时候后端再传到阿里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)

npm install @alicloud/sts-sdk

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

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控制台按钮

[图片上传失败...(image-dd7166-1637413964015)]

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

image.png

完善服务端提供的数据

这个时候其实已经拿到accessKeyIdaccessKeySecretstsTokenexpiration这四个参数了

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

这个bucket其实就是对应的使用的那个bucket,这个可以在阿里云对象存储页面看到,有一个bucket列表,就是你要是用的那个bucket的名字

region就是某一个bucket所在的地域,比如我这个就是oss-cn-beijing

[图片上传失败...(image-c0860a-1637413964015)]

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

{
  accessKeyId,
  accessKeySecret,
  stsToken,
  bucket,
  region,
  expiration
}

前端的工作

好了,我们的后端同学的工作已经完成了~

前端er们来跟我 左边一起画个龙 在你右边 画一道彩虹(bushi)

首先我们也新建一个oss-sts-client.js/ts,然后安装一个ali-sdk/ali-oss: Aliyun OSS(open storage service) JavaScript SDK for the browser and Node.js (github.com)的包,对了不支持IE10和之前的IE版本啊

npm install ali-oss --save

然后复制下面的内容到这个文件中,用js的同学可以把ts相关的代码删掉(赶紧换到TS吧,再不换没人跟你玩了)

// 这个是服务端提供给前端的一个请求接口,返回上面我们提到的几个参数
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过期了没有,如果没有过期,还是用这个实例进行上传操作,如果过期了,重新生成一个实例!

这里我们就拿一个简单的上传小文件为例(大文件分片上传,和上传成功回调(需要后端同学提供回调地址) 可以自己去看文档,我就不展开细说了)

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的维护策略,各位就仁者见仁智者见智吧,方案很多,怎么贴合业务怎么来,但是不推荐往localStoragesessionStorageindexDB里面存STS token等那些参数,你怎么就确定你的用户不是一名前端er呢?

CORS的问题

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

image.png

image.png

总结

笔者也是在接触阿里云的前端直传文档之后和后端同学看文档看到头皮发麻 麻了彻底麻了 麻中麻之后,总结一篇前后端流程都可以打通的OSS前端直传的文章,如果有问题,欢迎在评论里讨论,如果能帮助到你,给咱个三连吧

519ae95b7f18308175d22538de7aa8e6.jpeg
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容