HTML5怎样上传图片并压缩_HTML5图片上传压缩法【压缩】

FileReader 是唯一能将文件转为 data URL 的标准 API,但 readAsDataURL 易致大图 OOM;需在 Image.onload 后绘图,用 toBlob 压缩并控制格式质量,按最长边等比缩放,解析 EXIF 修正方向,iOS 需额外处理偏色。

FileReader 读取图片再转成 canvas 压缩

原图直接上传体积大、耗流量,浏览器端压缩得先把它变成可操作的像素数据。FileReader 是唯一能从 input[type="file"] 获取图片二进制并转为 data URL 的标准 API。但注意:readAsDataURL 会把整个文件加载进内存,大图(比如 10MB 手机照片)容易卡顿甚至 OOM。

实操建议:

  • 监听 inputchange 事件,取 e.target.files[0],优先校验 type 是否为 image/jpegimag

    e/png
  • FileReader.readAsDataURL() 读取后,在 onload 回调里创建 Image 对象,src 设为该 data URL
  • Image.onload 触发后,才能安全地画到 canvas 上——过早绘图会导致 canvas 空白
  • 压缩逻辑放在 Image.onload 内,避免跨域问题(本地文件无跨域,但若后续改用 URL 加载远程图就得加 crossOrigin="anonymous"

canvas.toBlob() 控制质量与格式,比 toDataURL() 更省内存

toDataURL() 返回 base64 字符串,体积比二进制大 33%,且无法流式处理;而 toBlob() 直接生成 Blob 对象,可直接传给 FormData 上传,也支持指定质量(仅对 image/jpegimage/webp 有效)。

常见错误现象:设了 quality: 0.7 但 PNG 图没变小——因为 PNG 不支持质量参数,toBlob() 会忽略它,仍输出无损 PNG。

实操建议:

  • 统一转成 image/jpeg 再压缩,兼容性好、体积小;若需透明通道,才保留 PNG 并改用尺寸缩放降质
  • 调用写法:
    canvas.toBlob(blob => {
      const formData = new FormData();
      formData.append('file', blob, 'compressed.jpg');
      // 后续 fetch 上传
    }, 'image/jpeg', 0.8);
  • 质量值建议 0.6–0.8:低于 0.5 易出现明显色块,高于 0.8 压缩收益极低

按目标尺寸缩放而非固定宽高比,避免拉伸或裁剪

很多教程直接设 canvas.width = 800; canvas.height = 600;,结果人像被压扁。正确做法是保持原始宽高比,按最大边限制缩放(例如“最长边 ≤ 1200px”)。

性能影响:不缩放只改质量,对 4000×3000 图压缩后仍可能有 2MB;缩放到 1200px 最长边后,同样质量下通常压到 300KB 以内。

实操建议:

  • 计算缩放比例:scale = Math.min(maxWidth / img.width, maxHeight / img.height),其中 maxWidthmaxHeight 取相同值(如 1200)即实现等比约束
  • canvas 宽高设为 Math.floor(img.width * scale)Math.floor(img.height * scale),避免小数导致渲染模糊
  • ctx.drawImage(img, 0, 0, canvas.width, canvas.height) 拉伸绘制,不加额外坐标参数

移动端真机测试时注意 EXIF 方向和 iOS Safari 的 canvas 渲染 bug

手机拍的照片带 EXIF 旋转信息(如 Orientation: 6 表示顺时针转 90°),但 canvas.drawImage() 默认忽略它,导致上传后图片歪着。iOS Safari 还有个经典 bug:横屏拍摄的 JPEG 在 canvas 上绘制后颜色偏灰、对比度下降。

容易被忽略的地方:

  • 必须用 exif-jspiexifjs 解析 file 的 EXIF,根据 Orientation 值动态调整 canvas 绘制逻辑(比如旋转 canvas context 或交换宽高)
  • iOS Safari 下,强制用 image/jpeg 格式 + 质量 0.9 以上可缓解偏色;更稳方案是上传前用 createImageBitmap()(支持 orientation 自动修正,但兼容性限于较新版本)
  • 真机调试别只信 Chrome DevTools 的 device mode——它不模拟 EXIF 和 GPU 渲染差异