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);
            },
            });
        })
}

测试一波,

vant1

原图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)
}

测试。

vant1

可以看到 size 确实比之前小了很多,只有200k了。

PS:

  • 对于 compressorjs 的参数可以按需调整

总结

这里其实费时间的主要是多图压缩的封装,其他都不怎么费事。遇到问题,如果是比较常用的,我的建议是先去搜索,搜索有两块,一块浏览器,一块是 github 项目的 issue。一般都能找到答案,但也可能并不能解决,我们就自己查找文档甚至阅读源码。剩下就是代入测试。


Vant多图压缩后上传的解决过程
https://blog.puresai.com/2023/07/25/491/
作者
puresai
许可协议