本文要解决的问题 很多网站功能都会需要处理用户上传的图片,比如方让用户上传会员照片。
不过随着手机相机越来越好,拍出来的照片随便都是几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照片特别有用。
- 递回压缩逻辑
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