关于腾讯云COS对象存储前端大文件上传的探索与解决方案

方滴云WEB2023-05-14 00:24

项目背景


对于日异复杂的网络环境,静态资源选择云存储,绝对是最好的选择,相比传统的购买带宽,这种按流量付费的模式绝对是更有竞争力的。如果想更优惠,可以考虑购买资源包。

文件上传中大文件上传是一个绕不开的话题,当然我们也是一步步改进的,显示上传进度,文件分片等前后端技术,逐步在需求中被探索。

我们在之前的项目中,一般是通过限制文件上传大小来解决大文件上传的,本质来说,这是一个扯淡的解决方案。现在我们比较成熟的方案是统一上传接口,前端通过文件体积,自动判断是否分片。同时,对于大体积图片,还是在前端 进行相互素积压缩后,再走对应的上传逻辑。

遇上云之后,前端的分片,在后端的处理上就就得有些不那么顺手了。当然,也可能是腾讯云COS对象存储分片上传的规则问题吧。


探索过程


首先采用的当然是分片上传,腾讯云分片上传一共有五个相关的接口,初始化分片上传,拿到uploadId,随后执行分片上传,主要参数也就是第一步的uploadId与片编号, 这个返回可以拿到ETag;最后是完成分片任务,需要的核心参数是uploadId,与每一次分片的编号与Etag。

另外两接口主要是起查询作用的的,分别可查询已上传分片与待上传分片任务。

逻辑挺明朗的,但是基于之前的代码,改造成本太高,最后放弃了。其实腾讯云SDK里的upload方案也是一个可以自己识别小文件直接上传大文件分片上传的。于是我就想,偷懒得了,先上传到服务器,然后在服务器上调用其upload方法,最后删除本地文件。

几M的文件还马马虎虎的过得去,大了就有点尴尬了,因为他走了分片,所以不是正常的返回了,后续逻辑无法处理,不过可以使用消息列队,在列队里处理他。因我们发现,虽然看起来前台挂了,但是文件其实是传到云端了的。具体几M,我试的5M内都还行的。

爬出前面两个坑后,我们惊奇的发现, 有个追加上传的接口,而且这个接口与我们之前的逻辑几乎是完全贴合的,基本不用怎么动手。大概原理就是,每个分片走追加上传,需要的核心参考就是postition,这个参数很好算,就是分片乘以每片的大小有可以拿到了。

激动的一试,成功了,而且速度明显比之前快的。因为这里文件名用的是前端分片的文件名的MD,所以需要上传完成后将其改名,如于我们又找到了复制接口,这个接口主要的功能就是文件拷贝与修改。

于是,真正的悲剧开始了。这个文件上传后,其类型为:appendtype,这个东西让我们束手无策,他除了可以在删除外,移动改名,都不可以操作,文档里对他的介绍甚少,最后官方人员也只能说,这个类型就这样了,没有然后。这里浪费时间最多,最后也只好放弃了。


解决方案


最后我们使用的解决方案是WEB端直传,文档:https://cloud.tencent.com/document/product/436/9067。

简单的说就是,不使用官方的SDK,使用XML的API在前台直接把文件上传到云端,当然前端还是用到了官方提供一个权限验证的文件,具体可以参考文档下载。

这里主要有两个接口,POST与PUT,具体的区别从表现来看,我个人的理解就是POST更灵活些。不过,我因为有不同的后端框架,两个方法我们都使用了。

大体的逻辑是这样的,服务端创建临时密码,前端根据过期时间做缓存,前端在上传时去计算签名,之后拿到签名和 sessionToken,进行上传。如果使用 PutObject 接口上传文件,将计算得到的签名和 sessionToken,分别放到发请求时 header 的 authorization 和 x-cos-security-token 字段里。如果使用 PostObject 接口上传文件,则将计算得到的签名和 sessionToken,分别放到发请求时表单的 Signature 和 x-cos-security-token 字段里。

当然有一点一定要注意,使用POST请求时,file 字段必须放在整个表单的最后面。因为这个问题,我们浪费了很久的时间,官方给出的错误很不明确, 这个说明在POST的API文档中:https://cloud.tencent.com/document/product/436/14690

废话不多说了,下面就上代码了吧,因为我是两个不同的框架使用的,所以代码还是有一些区别的。不过逻辑基本是一样的。

获取COS临时密钥

计算签名

PUT方式上传文件

POST方式上传文件

因为参数名的有些不同,所以两个方法看起来不太一样。至此,前端的就说完了,当然后面就更简单了。

服务端生成临时密钥

这里有引用一个txcosSts.php文件,这个官方也有提供,基本拿着就可以直接用了。