近期对视频上传进行了相关优化,由原本的上传到后端服务器改为前端直传 all-oss。
出现的问题
阿里云提供了 ali-oss sdk,前端可以很方便的进行直传工作。
但实际使用中也出现了一些问题
取消分片上传
ali-oss 文档中仅提供了 abortMultipartUpload 来进行取消分片上传。
该方法有几个问题前端获取到的 access_token 有效期有一个小时,后端不希望暴露给前端 bucket 的删除权限
abortMultipartUpload 方法调用后会删除已有的上传碎片,后续无法做断点续传功能。
在源码中看到在 proto 挂了一个 cancel 方法,可以直接调用https://github.com/ali-sdk/ali-oss/blob/c87f6dab60de065e94e522fdc3bc0b27b94135e4/lib/common/thunkpool.js#L87github.com
另外在 readme 里找到了这样的一段话
//the other event to cancel, for example: click event//to cancel upload must use the same client instancestore.cancel();
OSS 实际上提示提供了一个 cancel 方法,但并没有很好的在文档中说明
关于此问题讨论在此:https://github.com/ali-sdk/ali-oss/issues/384github.com
Cannot read property 'crypto' of undefined
ali-oss sdk 的包大约在 500kb 左右,直接引用源码,有使用 generator,还需要配置 transform plugin 。
尝试直接引用 dist 目录下的 aliyun-oss-sdk.js ,发现会有报错
Cannot read property 'crypto' of undefined
查遍 ali-oss 也没有找到问题。
最后很怪异的在 aws-sdk-js 的 issues 中查找到了类似问题https://github.com/aws/aws-sdk-js/issues/1566github.com
把
改为
var _global = this || window || {}
可以解决问题。
应该是 crypto-browserify@ 1.0.9 出了问题。
阿里云工程师后续有修复计划,关于此问题的讨论在此:https://github.com/ali-sdk/ali-oss/issues/386github.com
此问题阿里云工程师已修复https://github.com/ali-sdk/ali-oss/pull/390github.com
包体积优化
在无法引用 aliyun-oss-sdk.js 之后,考虑到其他能够优化的方案。
Dynamic import
关于 Dynamic import 可以在这里了解:https://developers.google.com/web/updates/2017/11/dynamic-importdevelopers.google.comdynamically importing ES modules2ality.com
使用 Dynamic import 做动态加载,只有在上传视频操作时才加载 ali-oss 包。
看起来是个不错的选择,很容易就写出了这样的代码
class xxx extends xxx {
getClient = () => {
const {
uploadtoken: {
access_id: accessKeyId,
access_key: accessKeySecret,
access_token: stsToken,
},
} = this.video
return import('ali-oss').then(OSS => {
this.client = new OSS.Wrapper({
region: 'oss-cn-hangzhou',
bucket: BUCKET,
secure: true,
accessKeyId,
accessKeySecret,
stsToken,
})
})
}
uploadVideo = () => {
return this.getClient().then(() => {
this.client.multipartUpload(objectKey, this.value, {
progress: this.handleProgress,
mime: this.value.type,
callback,
partSize,
})
}
}
}
实际在项目中使用后,却报错无法使用
Unexpected token: name (Mime) [./node_modules/ali-oss/shims/mime.js:8,0]
[main.3.946eb92154661ba849d7.js:10626,8]
原因是直接引用源码,需要添加 generator transform。虽然可以解决问题,但并不能满足我们的优化策略。需要考虑其他方案。
关于此问题的讨论在此:https://github.com/ali-sdk/ali-oss/issues/362github.com
get script
ali-oss 提供了一个可以使用的 CDN 地址,在无法 import dist 的情况下,get script 也是一个比较好的方案。
把 Dynamic import 的代码改为 get script 即可
import memoize from 'lodash/memoize'
function getScript(source) {
return new Promise(resolve => {
let script = document.createElement('script')
const prior = document.getElementsByTagName('script')[0]
script.async = 1
script.onload = script.onreadystatechange = function(_, isAbort) {
if (
isAbort ||
!script.readyState ||
/loaded|complete/.test(script.readyState)
) {
script.onload = script.onreadystatechange = null
script = undefined
if (!isAbort) {
resolve()
}
}
}
script.src = source
prior.parentNode.insertBefore(script, prior)
})
}
const importScript = memoize(getScript)
class xxx extends xxx {
getClient = () => {
const {
uploadtoken: {
access_id: accessKeyId,
access_key: accessKeySecret,
access_token: stsToken,
},
} = this.video
return importScript('http://gosspublic.alicdn.com/aliyun-oss-sdk-4.13.2.min.js').then(OSS => {
this.client = new window.OSS.Wrapper({
region: 'oss-cn-hangzhou',
bucket: BUCKET,
secure: true,
accessKeyId,
accessKeySecret,
stsToken,
})
})
}
uploadVideo = () => {
return this.getClient().then(() => {
this.client.multipartUpload(objectKey, this.value, {
progress: this.handleProgress,
mime: this.value.type,
callback,
partSize,
})
}
}
}
callback
文档中解释 CallBack参数是由一段经过base64编码的Json字串。
但实际传入的时候直接传入对象即可,ali-oss 的 encodeCallback 进行了 base64编码 等操作。
https://github.com/ali-sdk/ali-oss/blob/master/lib/common/callback.jsgithub.com
分片大小
OSS 最大分片数量不能超过1000个,每个分片大小不能小于 100kb (最后一个分片除外)。
目前的方案是分成 50个分片,如果分成的分片小于 128kb,则分片大小设置为 128kb
分片太小会导致上传失败率增高,如果数据异常则会尝试增加分片的大小。
对于低于 10M 的视频,后续可以考虑不采用分片上传的方法。
断点续传
ali-oss sdk 提供断点续传的方法,关于断点续传,主要有两种思路checkpoint保存到浏览器的localstorage,下次再调用的时候传入checkpoint参数
浏览器不做操作,checkpoint保存在后端,上传之前先去先去服务端请求查看是否有保存的 checkpoint
相比较来说,第一种方法实现起来更简单,也比较方便,不需要后端做额外的操作。
其他总结
直传 ali-oss 让视频上传速度提高了一倍,但转码速度依旧很慢,后续需要针对转码进行额外的优化。