前端压缩图片必须走filereader→image→canvas→toblob()链路,因file是只读二进制blob,无宽高、不可缩放设质量;需先解码为image实例才能获取naturalwidth/height、等比缩放绘制,toblob()质量参数仅对jpeg/webp生效,png无效,且必须用naturalwidth/height防变形,压缩后须校验blob.size确认实效。

前端压缩图片不是“加个参数就能压”,而是必须走 FileReader → Image → canvas → toBlob() 这条链路,跳过任意一环都可能出错或压不动。
为什么不能直接用 file 压缩?
原始 File 对象是只读的二进制 Blob,没有宽高、无法缩放、也不能设质量。你得先把它解码成可操作的图像数据——也就是加载为 Image 实例,才能读取尺寸、绘制到 canvas 上。
-
FileReader.readAsDataURL()是唯一能安全把File转成可加载 URL 的方式;readAsArrayBuffer()或直接传file给Image.src会失败 - 别在
img.onload外调用canvas.drawImage(),否则画布为空,toBlob()输出的是透明图 - 移动端 iOS Safari 对
toBlob()支持晚于 Chrome,若需兼容旧版 iOS,得 fallback 到toDataURL()再手动转 Blob
canvas.toBlob() 的质量参数陷阱
toBlob(callback, mimeType, quality) 的 quality 只对 image/jpeg 和 image/webp 生效,传给 image/png 完全被忽略——PNG 压缩靠的是像素降采样或索引色,不是质量值。
- 明确指定
'image/jpeg',哪怕原图是 PNG,否则 iOS 可能默认导出 PNG(体积翻倍) -
quality推荐从0.7起试,低于0.5易出现明显色块,高于0.85体积下降不明显 - 某些安卓 WebView 对
quality解析异常,建议加兜底逻辑:压缩后若体积仍 > 目标值,再降一次质量重压
怎么控制输出尺寸又不拉伸变形?
核心是按比例缩放,不是硬设 canvas.width/height。原始宽高比(img.naturalWidth / img.naturalHeight)必须保留,否则头像变椭圆、商品图被压扁。
- 先算最大允许宽高(比如
MAX_WIDTH = 1200,MAX_HEIGHT = 800) - 再按「等比缩放后不超过任一上限」计算目标尺寸:
const ratio = Math.min(MAX_WIDTH / img.naturalWidth, MAX_HEIGHT / img.naturalHeight);const width = img.naturalWidth * ratio;const height = img.naturalHeight * ratio; - 别用
img.width或img.height(那是 CSS 渲染尺寸),必须用img.naturalWidth和img.naturalHeight
上传前怎么确认压缩真生效?
别只看文件名或 Base64 长度——toBlob() 后拿到的是新 Blob,它的 .size 才是真实体积。很多 bug 出现在「以为压了,其实 canvas 没 redraw 或 mimeType 写错」。
- 压缩后立即打印
blob.size,和原始file.size对比,差值应 > 30% - 用
URL.createObjectURL(blob)创建临时预览 URL,插入<img>标签肉眼核对是否模糊/变形/偏色 - 如果压缩后体积反而变大,大概率是用了
image/png导出高清 JPEG 原图,或 canvas 尺寸设得比原图还大
最常被跳过的一步:没检查 img.naturalWidth 是否为 0——某些 WebP 或损坏图加载后 onload 触发但尺寸无效,得加 if (img.naturalWidth === 0) { reject('Invalid image'); } 防止白屏或空上传。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











