React+腾讯云COS对象上传 -- 记录一次云服务上传的踩坑之旅

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 15:51   2307   0

踩坑缘由:公司新项目打算使用云服务上传文件,减少自己服务器的压力。但是之前并没有接触过相关经验,所以遇到了一些问题,打算整理一下供后期新人参考或者一些其他浏览人员参考。

项目集成:react+ant design pro+dva+braft editor(react较好用的富文本库,界面比较简洁,拓展性高,重点是和antd有较高的默契)

文件上传功能主要包含在antd的upload组件和braft editor第三方富文本库中的媒体上传功能中。

一开始想的是,直接让后台把url拼好给我,然后使用put/post上传的方式把file文件传过去。但是在腾讯云文档上没有找到FormData接受的文件字段名称。调用的时候一直报403错误,打开控制台发现有跨域错误(注意这是一个配置的坑)。

其次查了一遍文档发现有js对应的SDK,于是采用的SDK上传的方法

为了测试走通,我先用的永久密钥的方式

按照官网的案例,安装了SDK,import并实例化了COS对象,然后调用了putObject方法上传文件,发现请求还是403,console中还是有跨域问题(因为现在问题已经解决,无法重现错误了,就先不截图了)。

发现不对劲,就找后端排查一下,后来发现配置的时候白名单没有添加。

配置完成后,上传走通,nice!!!

好了,上传能走通了,我们来优化一下,使代码更安全一点,放弃永久会签的方式(因为如果使用永久会签,别人会在请求参数里面截取你的腾讯云账号密码信息,导致企业账号丢失)。采用前端调用后端接口,生成临时会签的方法。

我们后端使用的也是SDK的方式,文档中有详解,就不补充了。

开始上传业务组件封装。

upload.js(部分)

 photoRequest = async (files) => {
        const _this = this
        const { file } = files
        const filename = file?.name ? getHashCode(file?.name) : "";
        const uploadUrl = `/${this.props.pathname}/${filename}`
        cos = instanceCos(uploadUrl);
        cos.putObject({
            Bucket: 'your Bucket', /* 必须 */
            Region: 'your Region',     /* 存储桶所在地域,必须字段 */
            Key: uploadUrl,              /* 必须 */
            StorageClass: 'STANDARD',/* 可以固定写死 */
            Body: file, // 上传文件对象
            onProgress: function (progressData) {
                _this.setState({
                    loading: true
                });
            }
        }, function (err, data) {
            if (!err) {
                _this.setState({
                    imageUrl: `//${data?.Location}`,
                    loading: false
                }, () => {
                    _this.props.uploadSuccess &&         
                       _this.props.uploadSuccess(_this.state.imageUrl);
                    message.success("文件上传成功");
                })
            } else {
                if (err.statusCode !== 403 && err.Messag !== "The Signature you specified is invalid.") {
                    _this.setState({
                        loading: false
                    });
                    return message.error("文件上传失败!")
                }
            }
        })
    }

代码解读:instanceCos函数是实例化COS对象,因为有其他组件也要使用,我就提取了出来。

?. 是es6的一个开发小技巧,原理我也说不明白,当存在对象多级取值的时候,如果中间值不存在,不会报错,会返回underfined,eg:this.props?.uploadFile?.file 如果uploadFile不存在,正常会报uploadFile原型不存在,使用?.就不会报错。

braftEditor.js(部分)

  myUploadFn = async (param) => {
        const { file, id } = param;
        const filename = file?.name ? getHashCode(file?.name) : "";
        const uploadUrl = `/${this.props.pathname}/${filename}`
        cos = instanceCos(uploadUrl);
        cos.putObject({
            Bucket: 'your Bucket', /* 必须 */
            Region: 'your Region',     /* 存储桶所在地域,必须字段 */
            Key: uploadUrl,              /* 必须 */
            StorageClass: 'STANDARD',
            Body: param.file, // 上传文件对象
            onProgress: function (progressData) {
                param.progress(progressData.percent * 100)
            }
        }, function (err, data) {
            if (!err && data) {
                param.success({
                    url: `//${data?.Location}`,
                    meta: {
                        id: id,
                        title: file?.name,
                        alt: file?.name,
                    }
                })
            } else {
                param.error({
                    msg: '上传失败'
                })
            }
        });
    }

                 <BraftEditor
                    controls={controls}
                    value={this.state.editorState}
                    onChange={this.handleChange}
                    extendControls={extendControls}
                    media={{ uploadFn: this.myUploadFn }}
                    contentStyle={{ height: 210, boxShadow: 'inset 0 1px 3px                    rgba(0,0,0,.1)', ...this.props.contentStyle }}
                />

写到braft editor不得不夸一下,cos的onProgress配合BraftEditor上传参数中的 param.progress,效果很友好,也很简单。

一开始的设想是打算把实例化COS放在componentDidMount中的,这样打开页面进行上传只需要进行一个会签调用。这样会遇到另外一个问题,浏览器端生成的会签和服务器端生成的会签不匹配,还是会报403会签失效的错误。

原因在于new COS()生成的会签需要一个文件路径的字段,这个路径指向的云上桶的路径(路径名+hash加密后的文件名,设置成动态文件名是为了方便取,避免发生同名文件覆盖现象)。既然要拼接上文件名,那么就不可能写在页面刚加载的时候了,也不可能提取出来了,只能每次调用的时候再请求一遍后端获取会签的接口。

目前,业务中文件上传还没存在文件很大的情况,所以还未做分片上传的功能,不过文档上有案例,可以参考着来。

开发还未结束,可能云上传还会遇到其他问题,解决后我会编写记录,希望看到本文章的人会有所帮助。

本人专注学习React相关技术栈,以后会不定时更新博客,刚开始写博客比较手生,欢迎关注收藏一起学习前端相关知识点。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP