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端,也会尽量给出更完美的解决方案。
不错,加油,继续
别的前端问怎么弄,就直接发链接过去。
前端的伙伴都应该来学习