Skip to content

图片压缩

在前端开发中,图片压缩是一个常见的需求。通过在客户端压缩图片,可以显著减少上传流量,加快上传速度,并减轻服务器的存储和带宽压力。

本文介绍两种实现方案:

  1. 使用成熟的第三方库 Compressor.js(推荐)。
  2. 使用原生 JavaScript (Canvas API) 手动实现。

方案一:使用 Compressor.js 库 (推荐)

Compressor.js 是一个轻量级的 JavaScript 图像压缩库,它使用浏览器的原生 canvas.toBlob API 进行压缩。

核心优势:自动处理了最棘手的 EXIF 方向 (Orientation) 问题(即手机拍摄的照片在 Canvas 中可能会倒置),并且 API 简洁易用。

在线演示

核心代码

javascript
import Compressor from 'compressorjs';

new Compressor(file, {
  quality: 0.8,
  maxWidth: 1920,
  maxHeight: 1080,
  mimeType: 'image/jpeg',
  success(result) {
    // result 是一个压缩后的 Blob 对象
    const formData = new FormData();
    formData.append('file', result, result.name);
    // 上传 formData...
  },
  error(err) {
    console.log(err.message);
  },
});

方案二:原生 JS 实现

如果你不想引入额外的依赖,或者只需处理简单的场景(不需要考虑 EXIF 旋转),可以使用原生的 Canvas API 来实现。

注意:下面的演示没有处理 EXIF 方向信息。如果你上传一张由手机竖屏拍摄的照片,可能会发现压缩后的预览图是横向倒置的,这就是原生实现最主要的坑。

在线演示

核心实现原理

  1. 使用 FileReader 读取文件为 DataURL。
  2. 创建 Image 对象加载图片。
  3. 创建 <canvas>,根据最大宽/高计算缩放后的尺寸。
  4. 使用 ctx.drawImage() 将图片绘制到 Canvas 上。
  5. 使用 canvas.toBlob() 导出压缩后的 Blob 对象。
javascript
const compressImageNative = (file, quality = 0.8) => {
  const reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onload = (event) => {
    const img = new Image();
    img.src = event.target.result;
    img.onload = () => {
      // 1. 计算目标尺寸
      const maxWidth = 1920;
      let width = img.width;
      let height = img.height;
      
      if (width > maxWidth) {
        height = Math.round(height * (maxWidth / width));
        width = maxWidth;
      }

      // 2. 创建 Canvas
      const canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext('2d');
      
      // 3. 绘制图片 (注意:未处理 EXIF 旋转)
      ctx.drawImage(img, 0, 0, width, height);

      // 4. 导出压缩后的 Blob
      canvas.toBlob(
        (blob) => {
          console.log('压缩完成', blob);
        },
        'image/jpeg',
        quality
      );
    };
  };
};

方案对比

特性Compressor.js (库)原生 JS (Canvas)
实现难度⭐ (极低,API 简单)⭐⭐⭐ (需处理异步链、尺寸计算)
EXIF 旋转自动处理 (内置逻辑读取并修正)需手动处理 (非常繁琐,需解析二进制数据)
包体积~6KB (Gzipped)0KB (无依赖)
功能丰富度高 (支持尺寸限制、类型转换、质量控制等)低 (需全部自行编写算法)
兼容性✅ 处理了 iOS Safari 大图限制等坑⚠️ 需自行处理各浏览器怪异行为
适用场景生产环境、移动端上传、对稳定性有要求极简单的后台管理工具、学习研究

总结:在生产环境中,强烈建议使用 Compressor.js 或类似的成熟库,因为手动处理 EXIF 方向信息(Orientation)和各种浏览器的 Canvas 兼容性问题(如 iOS Safari 的 Canvas 内存限制)是非常耗时且容易出错的。