图片压缩:用 Compressor.js 自动调整质量压缩至指定大小

100 阅读4分钟

本文要解决的问题 很多网站功能都会需要处理用户上传的图片,比如方让用户上传会员照片。

不过随着手机相机越来越好,拍出来的照片随便都是几MB,直接上传的话,运行也占空间。

虽然网上搜罗了很多图片压缩工具,但大多只能设定固定的压缩的质量,无法保证压缩后的文件大小满足需求。

本笔记文将使用 Compressor.js 套件,实作一个图片压缩功能,满足以下需求:

自动不同的压缩质量,直到文件尝试小于指定大小(例如:600KB)。 将图片转换为WebP格式。 长、宽限制最大尺寸。 这样就可以保证限制压缩后的图片既符合文件大小,又保证有良好的底部。

核心概念:递回压缩 一般网上的图片汇总只能一次性设定质量参数,本篇用了 comporess.js 之后,采用递回尝试的方式,执行方式如下:

从最高品质( quality = 1.0)开始压缩。 检查压缩后的文件大小。 如果目标大小超过600KB,则降低质量(减少0.05)后重新压缩。 步骤重复2-3,直到文件满足大小要求,或质量降到下限(0.40)。 这种方法能够在保证文件大小的前提下,要求保留图片质量。

引入 Compressor.js 首先,我们需要引入 Compressor.js 这个图片压缩套件。

它可以在浏览器端直接处理图片:劳动力支持:

上面是直接引用CDN的方式,官方文件也有其他引用方式,可以自己的需求引用。

建立HTML 我们需要一个文件上传的文件输入,以及一个用于预览压缩结果的div:

实作智慧压缩函式 这是整个方案的核心函式,接受四个参数:

file:要压缩的原始文件。 targetSize:目标文件大小,默认600KB。 floor:品质下限,默认0.40。 step:每次性能下降幅度,默认0.05。 function compressWithFloor( file, targetSize = 600 * 1024, floor = 0.4, step = 0.05 ) { return new Promise((resolve, reject) => { let q = 1.0;

const attempt = () => {
  new Compressor(file, {
    quality: q,
    mimeType: "image/webp", // 強制轉換為 WebP 格式
    maxWidth: 2560, // 最大寬度限制
    maxHeight: 1440, // 最大高度限制
    success(blob) {
      console.log(
        `q=${q.toFixed(2)}  size=${Math.round(blob.size / 1024)}KB`
      );
      if (blob.size > targetSize && q - step >= floor) {
        q = +(q - step).toFixed(2);
        attempt(); // 繼續壓縮
      } else {
        resolve(blob); // 符合大小或到達品質下限
      }
    },
    error(err) {
      reject(err);
    },
  });
};

attempt();

}); } 参数说明 1.强制输出WebP格式

mimeType: 'image/webp'将所有图片(包括PNG、JPEG)都转换为WebP。

WebP 是 Google 开发的现代图片格式。在标准下相同。文件大小通常比 JPEG 小 25-35%。

2.尺寸限制

maxWidth: 2560并且maxHeight: 1440保证图片不会超过2K解析度。

如果原图尺寸缩小,不会被放大;

如果增大,会等比例缩小。

这对于手机拍摄4K、8K照片特别有用。

  1. 递回压缩逻辑

if (blob.size > targetSize && q - step >= floor) { q = +(q - step).toFixed(2); attempt(); // 繼續壓縮 } 女士计划码检查两个条件:

文件是否仍大于目标大小? 品质是否还有下降空间? 只要1、2都符合,就会降低品质并重新尝试压缩。

Blob 转 Base64 辅助函式 为了在浏览器中预览和下载图片,或者调用 API 传输到文件,我们需要将压缩后的 Blob 对象转换为 Base64 格式:

function blobToBase64(blob) { return new Promise((resolve) => { const r = new FileReader(); r.onloadend = () => resolve(r.result); r.readAsDataURL(blob); }); } 整合上传与下载功能 最后,我们监听档案上传事件,执行压缩流程,并自动触发下载:

document.getElementById("upload").addEventListener("change", async (e) => { const file = e.target.files[0]; if (!file) return;

try { const compressed = await compressWithFloor(file, 600 * 1024, 0.4, 0.05); console.log("final size:", Math.round(compressed.size / 1024), "KB");

const base64 = await blobToBase64(compressed);
document.getElementById("preview").src = base64;

// 取出原始檔名(去掉副檔名)
const originalName = file.name.replace(/\.[^/.]+$/, "");
const newFileName = `${originalName}-compress.webp`;

// 建立下載連結並自動觸發
const link = document.createElement("a");
link.href = base64;
link.download = newFileName;
link.click();

} catch (err) { console.error("壓縮失敗:", err); } }); 档名处理逻辑 程序会自动处理档名:

删除原始副档名:file.name.replace(/.[^/.]+$/, "")。 加上-compress.webp后缀。 如:vacation.jpg→ vacation-compress.webp。

压缩流程示例 假设上传一张5MB的照片,压缩过程可能如下:

q=1.00 size=1200KB → 超過 600KB,繼續 q=0.95 size=950KB → 超過 600KB,繼續 q=0.90 size=750KB → 超過 600KB,繼續 q=0.85 size=580KB → 符合要求,完成! final size: 580 KB 如果原图质量很差,即使降到最低质量仍超过 600KB,程序另外在q=0.40时停止,避免过度压缩导致图片难以排序。

完整计划码 以下是完整的示例:

Let's write - 圖片壓縮

Let's write - 圖片壓縮工具

筆記文:

<script src=""></script>
<script>
  function compressWithFloor(
    file,
    targetSize = 600 * 1024,
    floor = 0.4,
    step = 0.05
  ) {
    return new Promise((resolve, reject) => {
      let q = 1.0;

      const attempt = () => {
        new Compressor(file, {
          quality: q,
          mimeType: "image/webp",
          maxWidth: 2560,
          maxHeight: 1440,
          success(blob) {
            console.log(
              `q=${q.toFixed(2)}  size=${Math.round(blob.size / 1024)}KB`
            );
            if (blob.size > targetSize && q - step >= floor) {
              q = +(q - step).toFixed(2);
              attempt();
            } else {
              resolve(blob);
            }
          },
          error(err) {
            reject(err);
          },
        });
      };

      attempt();
    });
  }

  function blobToBase64(blob) {
    return new Promise((resolve) => {
      const r = new FileReader();
      r.onloadend = () => resolve(r.result);
      r.readAsDataURL(blob);
    });
  }

  // 依序嘗試 q=1.00, 0.95, 0.90, ... ,直到 <=600KB 或 q<0.40
function compressWithFloor(
  file,
  targetSize = 600 * 1024,
  floor = 0.4,
  step = 0.05,
) {
  return new Promise((resolve, reject) => {
    let q = 1.0;

    const attempt = () => {
      new Compressor(file, {
        quality: q,
        mimeType: "image/webp", // 強制轉 WebP
        maxWidth: 2560, // 最大寬度限制
        maxHeight: 1440, // 最大高度限制
        success(blob) {
          console.log(
            `q=${q.toFixed(2)}  size=${Math.round(blob.size / 1024)}KB`,
          );
          if (blob.size > targetSize && q - step >= floor) {
            q = +(q - step).toFixed(2);
            attempt(); // 繼續壓縮
          } else {
            resolve(blob); // 符合大小或到達品質下限
          }
        },
        error(err) {
          reject(err);
        },
      });
    };

    attempt();
  });
}

// Blob -> base64
function blobToBase64(blob) {
  return new Promise((resolve) => {
    const r = new FileReader();
    r.onloadend = () => resolve(r.result);
    r.readAsDataURL(blob);
  });
}

document.getElementById("upload").addEventListener("change", async (e) => {
  const file = e.target.files[0];
  if (!file) return;

  const targetSize = 600 * 1024;

  // 檢查原始檔案大小
  if (file.size <= targetSize) {
    console.log(
      `原始檔案大小 ${Math.round(file.size / 1024)}KB,已小於目標大小 ${Math.round(targetSize / 1024)}KB,不需壓縮`,
    );
    alert(`圖片大小已符合要求 (${Math.round(file.size / 1024)}KB),無需壓縮`);

    // 仍然顯示預覽
    const base64 = await blobToBase64(file);
    document.getElementById("preview").src = base64;
    return;
  }

  try {
    const compressed = await compressWithFloor(file, targetSize, 0.4, 0.05);
    console.log("final size:", Math.round(compressed.size / 1024), "KB");
    const base64 = await blobToBase64(compressed);
    document.getElementById("preview").src = base64;
    const originalName = file.name.replace(/\.[^/.]+$/, "");
    const newFileName = `${originalName}-compress.webp`;
    const link = document.createElement("a");
    link.href = base64;
    link.download = newFileName;
    link.click();
  } catch (err) {
    console.error("壓縮失敗:", err);
  }
});

</script>
演示与原始码 想要实际体验图片压缩工具的话,以下是完整的演示页面和原始码供大家使用。

使用前,如果这个工具对你有帮助,欢迎到GitHub给我一颗星星⭐,你的小小动作,对本站都是很大的鼓励。

演示:演示

GitHub 原始码:GitHub 存储库

前端(32 部分系列) 1 CustomEvent自定义一个addEventListener 2 表单验证:中文姓名、身体分证、手机条码、生日、信箱、手机、选择县市 ... 另外 28 个零件... 31 使用pm2.web建立免费的PM2监控系统 三十二 图片压缩:用 Compressor.js 自动调整质量压缩至指定大小。查看更多www.mjsyxx.com