uniapp中阿里云oss直传踩坑记录

美好的一天又开始了。我看到今天要做的功能图片上传?
简单,打开 uniapp 官网两个 api:
1.uni.chooseImage;
2.uni.uploadfile。

uni.chooseImage({
                count: 9, //默认9
                sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
                sourceType: ['album'], //从相册选择
                success: function(res) {
      let random = Math.round(new Date() / 1000) + Math.ceil(Math.random() * 10);
                            uni.uploadFile({
                                url: this.getOssInfo.host,
                                filePath: res[0],
                                name: 'file',
                                formData: {
                                    key: `community/${random}.jpg`,
                                    policy: this.getOssInfo.policy,
                                    host: this.getOssInfo.host,
                                    OSSAccessKeyId: this.getOssInfo.accessid,
                                    success_action_status: '200',
                                    Callback: this.getOssInfo.callback,
                                    Signature: this.getOssInfo.signature
                                },
                                success: uploadFileRes => {
                                    console.log(uploadFileRes);
                                }
                            });
            });

内心 os:就这?搞定收工!

but 上传跨域。

(图是网上找的,不要在意这些细节。)
这时不要着急还没到你的坑,联系后端,放开阿里云 oss 的域名限制。

再试一次,成功了。我开心去找产品炫耀刚做完的功能。然后发生了以下对话(以下对话纯属虚构,如有雷同,那肯定是......正常):

产品:听说你的图片上传好了?我来试试?

我:please.

产品:嗯?上传速度怎么这么慢?你没压缩吗?这么大的图片不压缩吗?用户体验肯定不好,给我压缩到 3m 以下!

我:嗯~这个~我...

产品:我不管,我就要!

我:fine fine fine,enough,I got it! I'll try!!!




此时要感谢一位不认识的兄弟的友情赞助。
附上插件连接
https://ext.dcloud.net.cn/plugin?id=1718

(插件市场的一位好心朋友),在我找了那么多压缩的插件之后发现只有这个满足需求,遂引入,修改,调试。

压缩成功,而且压缩效率很高。看看效果先。
代码:

//压缩图片
                    uni.showLoading({ title: '图片上传中...', mask: true });
                    _this.$refs.wCompress
                        .start(res.tempFilePaths, {
                            pixels: 2400000, // 最大分辨率,默认二百万
                            quality: 0.8, // 压缩质量,默认0.8
                            type: 'jpg', // 图片类型,默认jpg
                            base64: false // 是否返回base64,默认false,非H5有效
                        })
                        .then(res => {
                            console.log(res, 'res');
                            //这里默认上传为jpg格式的
                            let imgarray = [];
                            for (let i in res) {
                                let _fileBlob = _this.dataURLtoBlob(res[i]);
                                let random = Math.round(new Date() / 1000) + Math.ceil(Math.random() * 10);
                                let fileOfBlob = new File([_fileBlob], random + '.jpg');
                                _this.uploadImg(fileOfBlob);
                            }
                        })
                        .catch(e => {
                            console.log(e);
                            uni.hideLoading();
                        });

效果展示:

原图 11m 被压缩到七百多 kb!!!

正当我满足于他的压缩效率时,新的问题又出现了,我发现它压缩完成之后图片是一个 base64 文件。

然而 uni.uploadfile 不支持 base64 文件上传,而且要的只是一个临时路径。哈?那我压了个寂寞?


所以此时我选择放弃了 uni.uploadfile。 直接使用接口向阿里云 oss 上传。 那就需要将 base64 文件转成 blob 文件,这个简单以下方法即可:

let _fileBlob = _this.dataURLtoBlob(res[i]);
let random = Math.round(new Date() / 1000) + Math.ceil(Math.random() * 10);
let fileOfBlob = new File([_fileBlob], random + '.jpg');
dataURLtoBlob(dataurl) {
            console.log(dataurl, 'dataurl');
            var arr = dataurl.split(','),
                mime = arr[0].match(/:(.*?);/)[1],
                bstr = atob(arr[1]),
                n = bstr.length,
                u8arr = new Uint8Array(n);
            while (n--) {
                u8arr[n] = bstr.charCodeAt(n);
            }
            return new Blob([u8arr], { type: mime });
        },

然后使用 uni.request 请求向 oss 上传,but 新的问题又一次出现,

uni.request 只支持 String,Object,ArrayBuffer 这几种格式的 data 参数,而我们上传图片需要用到的格式是 formdata 格式。此时我已经接近放弃治疗。

尝试其他解决方案无果后,我又开始了骚操作,我安装了 axios,针对这个接口特殊处理使用 axios 去调用接口。

function doData(Param, File) {
    let request = new FormData();
    request.append('key', `community/${File.lastModified}.jpg`);
    // policy规定了请求的表单域的合法性
    request.append('policy', Param.policy);
    request.append('host', Param.host);
    // Bucket 拥有者的Access Key Id
    request.append('OSSAccessKeyId', Param.accessid);
    // 让服务端返回200,不然,默认会返回204
    request.append('success_action_status', '200');
    // 根据Access Key Secret和policy计算的签名信息,OSS验证该签名信息从而验证该Post请求的合法性
    request.append('Callback', Param.callback);
    request.append('Signature', Param.signature);
    // request.append('rand', random);
    // request.append('access_token', token);
    // request.append('signature', md5(token + postfix + Config.key + random));
    // 需要上传的文件filer
    request.append('file', File);
    return request;
}

OSSUpload(url, param, File) {
        let formData = doData(param, File);
        return new Promise((resolve, reject) => {
            axios({
                    url: url,
                    method: 'post',
                    data: formData,
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    }
                })
                .then(res => {
                    resolve(res.data.data)
                })
                .catch(err => {
                    reject(err)
                })
        })
    }

终于看见了成功的曙光,但是你以为这就完了吗?年轻人。

突然我想试试小程序能不能成功,然后就又双叒叕一次的出现了新的问题。小程序压缩完图片之后返回的不是一个 base64 文件。


而是一个临时路径??? (这个时候请原谅我没有想到直接用这个临时路径上传 oss)。

于是我又打开插件的文档发现有一个设置项,返回 base64。


设置之后再次尝试,嗯确实返回了 base64 文件,但是又有了新的报错信息,

小程序不支持 blob 对象,无法将 base64 文件转成 blob 文件。 我的心又凉了半截,告知产品小程序无法压缩?不这不是我的风格! 就在我焦虑的时候,突然茅塞顿开,临时路径???这不就是 uni.uploadfile 所需要的那个东西吗?

//#ifdef H5
                            //这里默认上传为jpg格式的
                            let imgarray = [];
                            for (let i in res) {
                                let _fileBlob = _this.dataURLtoBlob(res[i]);
                                let random = Math.round(new Date() / 1000) + Math.ceil(Math.random() * 10);
                                let fileOfBlob = new File([_fileBlob], random + '.jpg');
                                _this.uploadImg(fileOfBlob);
                            }
                            // #endif
                            //#ifdef MP-WEIXIN
                            let random = Math.round(new Date() / 1000) + Math.ceil(Math.random() * 10);
                            uni.uploadFile({
                                url: _this.getOssInfo.host,
                                filePath: res[0],
                                name: 'file',
                                formData: {
                                    key: `community/${random}.jpg`,
                                    policy: _this.getOssInfo.policy,
                                    host: _this.getOssInfo.host,
                                    OSSAccessKeyId: _this.getOssInfo.accessid,
                                    success_action_status: '200',
                                    Callback: _this.getOssInfo.callback,
                                    Signature: _this.getOssInfo.signature
                                },
                                success: uploadFileRes => {
                                    console.log(uploadFileRes);
                                    if (_this.coverList.length < 9) {
                                        _this.coverList.push(_this.getOssInfo.host + JSON.parse(uploadFileRes.data).data.filename);
                                    } else {
                                        util.toast('已上传9张封面图不能再上传了');
                                    }
                                    uni.hideLoading();
                                }
                            });
                            //#endif

遂条件编译针对小程序这样处理,尝试直接一次性成功。 至此 uniapp 阿里云 oss 图片直传功能才算结束。

针对这次经历我总结了一个道理,那就是平时和产品关系好点,必要时候可以砍掉一些你不想做的功能。

算了功能还是得做,还是自己多看看文档吧!!!

ps:文中有些解决方案并不是最完美的,比如引入axios,或者将base64文件转成blob文件等操作都是不太完美的解决方案,而且目前只是针对h5和小程序的图片上传,app端还没有测试。此文只是记录下当时的踩坑记录,后续会测试app端,也会尽量给出更完美的解决方案。

讨论数量: 3

不错,加油,继续

4年前

别的前端问怎么弄,就直接发链接过去。

4年前

前端的伙伴都应该来学习

4年前

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!