Vant多图压缩后上传的解决过程
前一阵用 Vant 写一个项目,遇到多图压缩上传的问题,今天就解决过程做一个简单的分享,希望对你有所帮助。
使用 van-uploader 组件
<van-uploader v-model="images"
multiple
:after-read="afterRead"
upload-icon="plus"
/>
afterRead 实现上传操作,对应的后端代码较简单,我这里就省略了(其实就是返回状态和地址)。这里为了效果,增加了上传中,上传成功这些状态。
const afterRead = (file) => {
if (file instanceof Array) {
file.forEach((v, i) => {
v.status = 'uploading';
v.message = '上传中...';
uploadImage(v.file).then((res)=>{
...
v.status = 'success';
v.message = '上传成功';
}).catch((err) => {
v.status = 'failed';
v.message = '上传失败';
})
})
} else {
file.status = 'uploading';
file.message = '上传中...';
uploadImage(file.file).then((res)=>{
console.log(res)
...
file.status = 'success';
file.message = '上传成功';
}).catch((err) => {
file.status = 'failed';
file.message = '上传失败';
})
}
}
压缩
压缩其实可以后端实现,也可以前端压缩后上传,还可以第三方sdk,这里为了简单(成本),直接选择前端压缩后上传。
前端压缩可以自己实现,我的js 水平一般般,这里直接使用现成的 compressorjs,这也是 Vant 推荐使用的。
照着官网的例子,我们发现 before-read 支持返回 Promise。
const beforeRead = (file) =>
new Promise(resolve => {
new Compressor(file, {
strict: false,
quality: 0.6, // 压缩质量
maxWidth: 1980, // 最大宽度
maxHeight: 1980,
convertTypes: ['image/jpeg'], // 转换格式
convertSize: 500000, // 500k 文件类型包含在convertTypes列表中的文件,其文件大小超过此值的文件将被转换为jpeg。
success: resolve,
error(err) {
showFailToast(err.message);
},
});
});
测试
上传单张,没毛病。上传多张就发现慢得很,为啥慢?一是没压缩,二是我的带宽小。
解决
这里是因为多张上传,file 其实是一个数组,没经过压缩。
那我想,那行,那我就判断一下:
if (Array.isArray(file)) {
let compressPromises = [];
file.forEach(function(v){
compressPromises.push(new Promise(resolve => {
new Compressor(file, {
strict: false,
quality: 0.6,
maxWidth: 1980,
maxHeight: 1980,
convertTypes: ['image/jpeg'],
convertSize: 500000,
success: resolve,
error(err) {
showFailToast(err.message);
},
});
}));
})
return compressPromises
} else {
return new Promise(resolve => {
new Compressor(file, {
strict: false,
quality: 0.6,
maxWidth: 1980,
maxHeight: 1980,
convertTypes: ['image/jpeg'],
convertSize: 500000,
success: resolve,
error(err) {
showFailToast(err.message);
},
});
})
}
测试一波,
原图2.5M,这里还是2M多。这又是为毛????
before-read 确实支持返回 Promise,但我们上面的compressPromises并不是 Promise,而是一个 Promise 数组。
继续改,怎么把这些 Promise 合起来呢?
查看Promise 文档,有这个方法 Promise.all() 。
继续改:
既然通用,我们不妨稍微封装下。
import Compressor from 'compressorjs'
import { showFailToast } from 'vant'
function compressorOne(file){
return new Promise(resolve => {
new Compressor(file, {
strict: false,
quality: 0.6,
maxWidth: 1980,
maxHeight: 1980,
convertTypes: ['image/jpeg'],
convertSize: 500000,
success: resolve,
error(err) {
showFailToast(err.message);
},
});
})
}
export function compressor(file){
if (Array.isArray(file)) {
let compressPromises = [];
file.forEach(function(v){
compressPromises.push(compressorOne(v));
})
return Promise.all(compressPromises)
} else {
return compressorOne(file)
}
}
beforeRead 这里直接调用封装的 compressor
const beforeRead = (file) => {
return compressor(file)
}
测试。
可以看到 size 确实比之前小了很多,只有200k了。
PS:
- 对于 compressorjs 的参数可以按需调整
总结
这里其实费时间的主要是多图压缩的封装,其他都不怎么费事。遇到问题,如果是比较常用的,我的建议是先去搜索,搜索有两块,一块浏览器,一块是 github 项目的 issue。一般都能找到答案,但也可能并不能解决,我们就自己查找文档甚至阅读源码。剩下就是代入测试。
Vant多图压缩后上传的解决过程
https://blog.puresai.com/2023/07/25/491/