前一阵用 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, 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。一般都能找到答案,但也可能并不能解决,我们就自己查找文档甚至阅读源码。剩下就是代入测试。