Antd upload压缩后,预览图模糊

Antd upload 压缩后,预览图模糊

需求:用户上传图片后,会用这个图片跑模型,所以图片大小会影响模型运行速度,需要尽量使图片在上传前先简单压缩一遍

我的方案:

因为前端使用了 Antd ,所以在 beforeUpload 的时候,1.用 canvas 绘图 2.再 toDataURL 的到 base64 或者 Blob 转成二进制 3.转成 File 进行上传

关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
beforeUpload: (file) => {
return new Promise((resolve) => {
// 图片压缩
let reader = new FileReader(),
img = new Image();
reader.readAsDataURL(file);
reader.onload = function (e) {
img.src = e.target.result;
};
img.onload = function () {
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
let originWidth = this.width;
let originHeight = this.height;

canvas.width = originWidth;
canvas.height = originHeight;
canvas.style.cssText = `width: ${originWidth}px; height: ${originWidth}px;`;
context.drawImage(img, 0, 0, originWidth, originHeight);
let compress = canvas.toDataURL(file.type, 0.65);
let arr = compress.split(',');
let bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
let imgFile = new File([u8arr], file.name, { type: file.type });
// File之后是不带uid的,没有uid antd会炸,所以把file的uid还给新的imgFile
imgFile.uid = file.uid;
resolve(imgFile);
};
});
};

将图片绘制到 canvas 中,再转成 File 对一般的图片都有压缩的效果,而且我代码中控制了 0.65 的 quality

但是最终效果如下:

打开预览:

发现 Upload 中的预览,和弹窗中的预览都是模糊的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
beforeUpload: (file) => {
console.log('beforeUpload', file);
// xxxxx其他代码

img.onload = function () {
// xxxxx 其他代码
let compress = canvas.toDataURL(file.type, 0.65);
let imgFile = new File([u8arr], file.name, { type: file.type });
// File之后是不带uid的,没有uid antd会炸,所以把file的uid还给新的imgFile
imgFile.uid = file.uid;
console.log('resolve-imgFile', imgFile);
console.log('compress');
resolve(imgFile);
};
};

打印结果如下:

发现用来上传到服务器的图片 size 为 1621386,转成 base64 后,用站长工具转成图片,发现这个 size1621386 的文件其实是清晰的,所以去服务端把上传的文件 down 下来果然是清晰的,所以很明显,这只是单纯前端显示的问题。

那么在handlePreview中打印出preview的文件看看

1
2
3
4
5
6
7
8
// Upload 组件的handlePreview 中加入打印
handlePreview = (file) => {
console.log('proview', file);
this.setState({
previewImage: file.url || file.thumbUrl,
previewVisible: true,
});
};

发现在预览的时候,图片 thumbUrl 变成了只有 41.2k ,显然这个拿来预览的图片有问题,拿出 base64 转出来看看,如下图,果然预览的是这个 41.2k 的高糊图片,且是被裁剪了的

原因:

AntdUpload组件中,ant-design/components/upload/utils.tsx 中,有previewImage这样一个函数,目的是把我们传过去的图片简单压缩成 200*200 的图片优化预览处的加载速度,但是previewImage的图片是结果beforeUpload压缩处理过的,所以导致图片被压缩再压缩,经历了两次压缩就变的很糊,尽管不会影响真实上传到后端的文件清晰度,但是会影响用户体验

解决办法:

自己写一个 previewFile,不对图片做任何处理,直接 return,这样预览的时候就不会走组件默认的previewImage了,并且把压缩一遍后的 base64 赋值给resolve出来的imgFile.url 这样在预览弹窗中看到的也是清晰的

并且需要在beforeUpload中,将压缩一遍的 base64 赋值给imgFileurl,这样resolveimgFileprefiewFile接收到,可以拿到正确预览的 base64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
beforeUpload = (file) => {
// xxxx省略代码
return new Promise(resolve => {
// xxxxx省略代码
// 没有uid 会炸
let compress = canvas.toDataURL(file.type, 0.65)
imgFile.uid = file.uid
imgFile.url = compress // 预览弹窗用压缩一遍的base64显示
resolve(imgFile)
}
}
// 不对图片做任何处理
previewFile = (file) => new Promise((resolve) => resolve(file.url || file.thumbUrl)), // 预览用清晰的图片

// xxxxxx省略代码

<Upload
beforeUpload={this.beforeUpload}
previewFile={this.previewFile}>
</Upload>

感谢阅读,勘误、纠错或其他请联系progerchai@gmail.com,或者点击这里提 issue 给我
欢迎交流 👏,你的每一次指导都可以让我进步