在现代 Web 应用中,图片往往是造成页面体积大的主要元凶。尤其在用户上传图片(头像、相册、商品图等)时,如果不做处理,一张高分辨率的照片可能达到几 MB,直接上传不仅费流量,还可能造成服务器压力、慢速网络下体验差。
因此,将图片在客户端预处理(压缩、缩放、重编码)已成为常见做法。Compressor.js 就是一个轻量、易用的前端图片压缩库,适合在浏览器端对用户上传的图片进行压缩。下面我们来深入了解它。
什么是 Compressor.js
Compressor.js 是一个在浏览器端运行的 JavaScript 图片压缩库。它利用 HTMLCanvasElement 的 toBlob / toDataURL 方法对图片进行“有损压缩(lossy compression)”,即在保证可接受画质的前提下减小文件体积。
该库支持对 JPEG / PNG / WebP 等常见图片格式进行压缩,还提供对尺寸约束、比例控制、旋转方向校正(EXIF 方向)等选项。
特点包括:
- 支持直接传入 File 或 Blob 对象进行压缩
- 压缩过程是异步的,使用回调或 Promise 风格处理
- 支持设置质量(quality)、最大/最小宽高(maxWidth、maxHeight、minWidth、minHeight)、是否保持 EXIF 信息(retainExif)、方向检测(checkOrientation)等
- 可配置“严格模式”(strict),即若压缩后的文件比原图还大,则返回原图而非压缩结果
Compressor.js 的官方版本发布在 GitHub,并以 MIT 协议开源。
Compressor.js 核心 API 与配置项
构造 API
new Compressor(fileOrBlob, options)
- fileOrBlob:必选。即待压缩的 File 或 Blob 对象
- options:可选。用于配置压缩行为的参数对象
常用配置项说明(部分)
在使用 new Compressor(file, options) 时,你可以给 options 对象传入多个配置项来控制压缩行为。这里列出几项常见并且实用的配置,以及它们的作用和默认值(若有):
- strict(布尔值,默认 true):如果压缩后的文件比原图更大,开启 strict 模式后会返回原图而不是压缩结果。但在某些情形下(比如 retainExif = true、指定了不同的 mimeType、输出尺寸大于原图、或 maxWidth/maxHeight 限制小于原图尺寸等)这个回退机制会被忽略。
- checkOrientation(布尔值,默认 true):表示是否读取 JPEG 图片的 EXIF 方向信息,并自动对图片进行旋转或翻转校正,以避免手机拍照方向错误的问题。对于非常大的文件(如大于 10 MB),有时建议禁用它以防内存崩溃。
- retainExif(布尔值,默认 false):压缩后是否保留原始图片的 EXIF 元数据(如相机型号、拍照时间、地理位置信息等)。保留 EXIF 会使压缩后体积略增,一般在业务有强烈需求才启用。
- maxWidth / maxHeight(数字型,默认分别为 Infinity):用来限制压缩后图片的最大宽度和最大高度。如果原图某一方向超过这个值,则会按比例缩放至该上限以内。
- minWidth / minHeight(数字型,默认分别为 0):设置压缩后图片的最小宽度和最小高度,下限通常用于避免压缩结果小得不可用。注意 minWidth/minHeight 不应超过 maxWidth/maxHeight。
- width / height(数字型,可选):如果你给定了输出的明确宽或高,Compressor 会尝试把图片缩放到这个尺寸(在保持比例或裁剪规则下)。如果你只指定一个维度,另一维度会根据原始宽高比自动计算。
- resize(字符串,可选值 “none”/“contain”/“cover”,默认 “none”):指定当同时设置 width 和 height 时,图片如何适应目标尺寸。比如 “contain” 表示缩放以保证整张图片都能显示在目标框内,“cover” 表示缩放/裁剪以填满目标框。若不设置 width/height,这个选项不会生效。
- quality(数字型,0 到 1 之间,默认约为 0.8):用于控制输出 JPEG 或 WebP 图片的压缩质量(越小体积越小但画质下降越明显)。如果该值设置为 1,有时可能使输出比原图还大。
- mimeType(字符串,默认 “auto”):指定输出图片的 MIME 类型,比如 “image/jpeg”、“image/png” 或 “image/webp”。默认情况下会沿用原图的类型(或根据浏览器能力决定)。需要注意的是在某些浏览器里把 PNG 转为 WebP 可能不被支持。
- convertTypes(字符串或字符串数组,默认 ["image/png"]):指明哪类图片类型(如 “image/png”)在超过某个大小阈值时要转换为其它格式(如 JPEG)。
- convertSize(数字型,默认值约 5 MB):如果图片的类型在 convertTypes 列表中,并且文件大小超过这个阈值,就会触发格式转换(比如把 PNG 转为 JPEG)。若你不希望任何类型发生转换,可将其设为 Infinity。
- beforeDraw(context, canvas)(函数,可选):在把图片绘制到 Canvas 之前会调用这个钩子,你可以在这里做一些背景填充、滤镜预处理等操作。参数包括 Canvas 的 2D 上下文 context 和 canvas 本身。
- drew(context, canvas)(函数,可选):在图片绘制到 Canvas 之后会调用这个钩子,适合用于添加水印、文字提示、边框等后处理操作。
- success(result)(函数,必选/回调):当压缩成功时会调用此回调,result 是输出的 Blob 或 File 对象。你可以在这里拿到压缩后的图片用于上传或展示。
- error(err)(函数,回调):若在压缩过程中出现错误(例如资源不足、浏览器不支持、图片加载失败等),会调用这个回调,err 为错误对象。
这些配置项组合起来,可以灵活地控制压缩后的图片在质量、尺寸、格式、方向、元数据等方面的表现。根据不同业务场景(如用户头像、商品图、相册上传等),你可以选择性开启或调整这些参数,以在文件体积和可用画质之间达到最优平衡。
注意:Compressor.js 默认行为包含“有损压缩 + 异步处理”,它不会无损压缩或保证完全保真。
Promise 风格封装
由于 new Compressor(...) 本身使用回调风格处理,一般我们会以 Promise 包装它,方便在现代 async/await 语法中使用:
function compressImage(file, options = {}) {
return new Promise((resolve, reject) => {
new Compressor(file, {
...options,
success(result) {
resolve(result);
},
error(err) {
reject(err);
}
});
});
}
这样就可以:
const compressed = await compressImage(file, { quality: 0.7, maxWidth: 1200, maxHeight: 1200 });
// compressed 是 Blob / File
Compressor.js 的使用代码示例
下面是一个比较完整的前端压缩 + 上传示例,适用于常见场景(如用户选择文件后自动压缩然后通过 AJAX 上传):
<input type="file" id="inputFile" accept="image/*">
<script type="module">
import Compressor from 'compressorjs';
document.getElementById('inputFile').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
try {
const compressedBlob = await new Promise((resolve, reject) => {
new Compressor(file, {
quality: 0.6,
maxWidth: 1600,
maxHeight: 1600,
checkOrientation: true,
convertSize: 200 * 1024, // 小于 200 KB 的图片不转换格式
success(result) {
resolve(result);
},
error(err) {
reject(err);
}
});
});
const formData = new FormData();
// 第三个参数用于指定文件名,否则部分后端不识别
formData.append('image', compressedBlob, file.name);
// 使用 fetch 或 axios 上传
const resp = await fetch('/api/upload', {
method: 'POST',
body: formData
});
console.log('Upload success', resp);
} catch (err) {
console.error('Compression failed:', err.message);
}
});
</script>
如果你在无 Ajax 的传统表单提交场景中使用,需要将压缩后图片转为 Base64 或用隐藏 input[type="hidden"] 存储后提交。示例(jQuery 风格):
$('#file').change(function(e) {
const file = e.target.files[0];
new Compressor(file, {
quality: 0.8,
maxWidth: 1600,
maxHeight: 1600,
success(result) {
const reader = new FileReader();
reader.readAsDataURL(result);
reader.onloadend = function() {
$('#hiddenImageField').val(reader.result); // hidden input name 同服务端字段
};
},
error(err) {
console.error(err.message);
}
});
});
服务端收到 Base64 字符串后再解码存储即可。
在现代框架(React / Vue / Angular)中,也可将上述逻辑包裹为 Hook / 组合函数 / 服务,便于在组件里使用。
性能与画质考量
在使用 Compressor.js 时,有几个关键点需要权衡与注意:
- 压缩质量 vs 体积:quality 值是压缩最直接的调控杠杆,越低体积越小,但画质下降越明显。一般范围可设在 0.5 ~ 0.8 之间,具体视图片内容和用户场景而定。
- 尺寸控制比压缩更有效:对于非常高分辨率图片(如手机拍照的几千像素),首先将其缩放至合理宽高(如最大边 1600、2000 px)比单纯用质量压缩更能节省体积且画质损失小。
- 严格模式(strict):若压缩后体积比原图反而大(可能在图片已很小或复杂纹理时发生),启用 strict 模式可优雅地回退到原图,避免“压缩反而更重”的尴尬。
- EXIF 方向修正:对于用户用手机拍照的图片,常常存在 EXIF 方向信息(横拍/竖拍方向),checkOrientation 设置为 true 可以自动修正。若不修正,可能出现图片旋转错误。
- 保留 EXIF 元数据:若业务场景需要保留 EXIF(如拍照时间、地理位置、相机型号等),启用 retainExif。但这会增加输出体积,应谨慎使用。
- 异步处理 & 卡顿风险:虽然压缩是异步的,但对大图(如几 MB 的图片)仍可能在主线程消耗一定时间。可以考虑将压缩任务放在 requestIdleCallback 或 Web Worker(如果你自己封装)中,以避免界面卡顿。
- 浏览器兼容性:Compressor.js 基于 Canvas + Blob API 实现,因此在现代浏览器中支持良好,但在极老旧环境可能需要兼容处理。
实战优化建议与注意事项
- 批量压缩时建议串行或分批:一次压缩大量高分辨率图片可能造成内存或性能异常。可做“队列 + 延时”处理。
- 根据文件大小动态调整策略:对于小图(如几 KB / 几十 KB),可以不压缩或只做尺寸调整;对于大图才做较激进压缩。
- 进度或用户提示:如果压缩时间较长,提供进度提示或“处理中”状态,以提升用户感知体验。
- SDK 封装 / 封装成服务:将压缩逻辑抽象为通用工具 / SDK(支持参数配置、错误捕获、重试、超时机制等)更便于复用。
- 后端校验与降级:即便前端压缩,也应在后端对上传图片做尺寸、格式、体积校验,以防用户绕过前端提交超大文件。
- 渐进增强 / 兼容降级:对于不支持 Blob / Canvas API 的旧设备,提供降级方案(例如不压缩上传或服务器端压缩)。
- 可结合 CDN / 图片处理服务二次优化:将预压缩 + 服务端 / CDN 端处理结合起来,达到更高压缩比和加载性能。
总结
Compressor.js 是一个轻量、使用简单、功能实用的前端图片压缩工具。通过合理选择 quality 与尺寸参数,并辅以异步处理和用户体验设计,你可以在用户上传环节截断大图流量,显著提升应用性能和用户体验。
在实际项目中,建议将其封装为可配置的工具模块/服务,并配合后端校验与 CDN 图像服务共同构建高效的图片处理链路。只要把握好“画质 vs 体积 vs 性能”的折中,就能在压缩与体验之间做到较优平衡。