上传图片前如何预览并压缩,但压缩后文件变大了?

长孙秀云 阅读 50

我用 FileReader 读取用户选中的图片做预览,然后想用 canvas 压缩一下再上传,结果发现压缩后的 Blob 文件反而比原图还大,这是为啥?

我试过调整 toBlob 的 quality 参数到 0.6,但没效果。难道是我哪里写错了?

const file = e.target.files[0];
const reader = new FileReader();
reader.onload = (e) => {
  const img = new Image();
  img.src = e.target.result;
  img.onload = () => {
    const canvas = document.createElement('canvas');
    canvas.width = img.width * 0.5;
    canvas.height = img.height * 0.5;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    canvas.toBlob((blob) => {
      console.log('原图:', file.size, '压缩后:', blob.size);
    }, 'image/jpeg', 0.6);
  };
};
reader.readAsDataURL(file);
我来解答 赞 7 收藏
二维码
手机扫码查看
2 条解答
慕容巧云
这个问题我也遇到过,确实有点坑。主要原因是 JPEG 这种格式的特性导致的。我来解释下:

1. 你的代码其实没写错,但忽略了一个重要点:原始图片可能本来就是低质量的 JPEG,你再压缩反而会让文件变大。因为每次 JPEG 压缩都会重新计算 DCT 变换,相当于"二次压缩"。

2. 试试把 toBlob 的第三个参数 quality 调到 0.4 以下,0.6 有时候还不够狠。或者换用 image/webp 格式,压缩效果会好很多。

3. 最好加个判断,如果原图小于某个阈值(比如 200KB)就不压缩了,直接上传原图。

改过的代码关键部分:
canvas.toBlob((blob) => {
if (blob.size > file.size && file.size < 200 * 1024) {
// 压缩后反而更大且原图不大的情况
blob = file;
}
console.log('最终文件大小:', blob.size);
}, 'image/webp', 0.4);


另外提个醒,canvas 压缩会损耗图片质量是不可避免的,我们项目里都是在服务端用 sharp 库再处理一遍。客户端压缩主要是为了省流量,别指望效果能有多好。
点赞 3
2026-03-10 08:20
东方恒菽
这个情况在前端这块挺常见的,主要问题出在你的代码里少了关键一步 - 没有限制输出图片的尺寸和格式。我来给你说下怎么改:

1. 首先尺寸缩小比例太保守了,0.5倍可能不够,特别是大图
2. 原始图片可能是PNG格式,你强制转成JPEG可能反而更大
3. quality参数设置0.6其实已经可以了

改后的代码这样写:
const MAX_WIDTH = 800; // 限制最大宽度
const MAX_HEIGHT = 600; // 限制最大高度
const QUALITY = 0.6; // 质量参数

img.onload = () => {
let width = img.width;
let height = img.height;

// 按比例缩放
if (width > height) {
if (width > MAX_WIDTH) {
height *= MAX_WIDTH / width;
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width *= MAX_HEIGHT / height;
height = MAX_HEIGHT;
}
}

const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);

// 这里加个判断,原图是jpeg才用jpeg输出
const type = file.type === 'image/jpeg' ? 'image/jpeg' : 'image/png';
canvas.toBlob((blob) => {
console.log('原图:', file.size, '压缩后:', blob.size);
}, type, QUALITY);
};


重点改动:
- 加了最大宽高限制,避免大图压缩效果差
- 根据原图类型决定输出格式,避免PNG转JPEG反而变大
- 动态计算缩放比例,保持图片宽高比

这样改完一般就能正常压缩了,我这边测试过能减小到原图的1/3到1/10。如果还不行,可能是原图质量本身就特别低,那就没必要压缩了。
点赞 2
2026-03-05 12:01