我做了一个纯浏览器端的图片压缩工具,不上传服务器

52 阅读4分钟

用纯浏览器实现「图片压缩」:不上传服务器、批量压缩、支持 PNG/JPEG/WebP(imgism.cn)

最近做了一个纯前端的在线工具站「一米集 / imgism.cn」,其中我最常用、也最想优先打磨的功能就是:图片压缩

很多时候我们只想把图片体积缩小一点(发微信、发邮件、上传到后台、放到文档里),但又不想把图片上传到陌生网站。于是我做了一个完全在浏览器本地完成压缩的小工具:上传图片后在前端用 Canvas 重新编码输出,整个过程不经过服务器。

本文会介绍网站整体信息与功能结构,并重点讲讲这个“纯浏览器图片压缩”的实现思路和使用方式。


网站是什么:一米集(imgism.cn)

一米集是一个纯静态站点(部署在 Cloudflare Pages),主打“打开即用、无需安装、尽量本地处理”的在线工具集合。

目前网站按分类提供:

  • 图片工具:压缩、裁剪、尺寸调整、旋转/翻转、格式转换、拼接、EXIF 查看/删除、图片↔Base64、OCR、抠图去背景、文字水印、图层合成、二维码生成、颜色提取等
  • PDF 工具:合并、拆分、提取页面、删除页面、PDF 转图片等
  • 文本工具:文本对比、文本文件对比、文本去重、字符统计、文本分析器(阅读/朗读时间、关键词密度等)
  • JSON 工具:格式化、压缩/转义、Diff、键排序等
  • 其他工具:计算器、正则测试、条码生成、年龄计算器、BMI 计算器、IP 信息查询等

网站首页入口:

图片压缩工具入口:


核心功能:纯浏览器图片压缩(不上传服务器)

这次重点介绍的工具是「图片压缩」:批量压缩并导出 PNG/JPEG/WebP,并支持打包下载。

它做了几件我自己很在意的事:

1)批量上传 + 预览

  • 支持拖拽多张图片上传,或者点击选择文件
  • 左侧显示图片列表(文件名、分辨率、原始体积)
  • 中间区域用 Canvas 做预览(预览会按最大边 720 做缩放,不影响最终导出)

2)输出格式可选:JPEG / PNG / WebP

  • JPEG:适合照片/大图,体积通常更小
  • PNG:更适合需要透明通道的图(logo/图标等),压缩幅度一般不如 JPEG/WebP
  • WebP:很多场景下兼顾体积与质量

另外我在工具里做了一个明确提示:

  • 转 JPEG 时,如果原图有透明背景,会使用白底合成(这是 JPEG 格式本身不支持透明导致的)
  • 需要保留透明背景,建议导出 PNG 或 WebP

3)质量可调(对 JPEG / WebP 生效)

质量范围 1~100。PNG 是无损重编码,所以质量参数不会生效(工具里也会做 UI 降亮与提示)。

4)下载方式:单张下载 + ZIP 打包下载

  • 每张图压缩后会显示“原始体积 → 压缩后体积 + 节省比例”
  • 支持单张下载
  • 支持把所有结果打包成 ZIP 一键下载(前端使用 JSZip)

怎么用:3 步完成压缩

  1. 打开图片压缩页:imgism.cn/tools/image…
  2. 拖拽/选择图片(可多张)
  3. 选择输出格式,必要时调整质量(JPEG/WebP),点击“压缩”,然后下载单张或 ZIP

隐私与安全:为什么说“不上传服务器”

这个工具的核心逻辑是:

  • 图片读取:FileReader 在浏览器中把文件读成 DataURL
  • 图片解码:new Image() 加载 DataURL 得到像素数据
  • 压缩导出:Canvas.drawImage() 后用 canvas.toBlob() 重新编码成指定格式
  • 下载:用 URL.createObjectURL(blob) 生成临时链接并触发下载

整个过程都发生在你的浏览器里,不需要把图片发送到服务端处理。


实现思路(前端版):Canvas + toBlob 重新编码

下面用一段“思路级”的伪代码描述实现流程(与实际代码结构一致):

// 1) 读取本地文件 -> Image
const img = await readFileAsImage(file); // FileReader + new Image()

// 2) draw 到 canvas
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);

// 3) 根据格式导出 blob
const mime = fmt === 'jpeg' ? 'image/jpeg'
  : fmt === 'webp' ? 'image/webp'
  : 'image/png';

const quality = fmt === 'jpeg' || fmt === 'webp'
  ? sliderValue / 100
  : undefined; // PNG 无损

const blob = await new Promise(resolve => {
  canvas.toBlob(resolve, mime, quality);
});

// 4) 下载(单张)
const url = URL.createObjectURL(blob);
download(url, filename);
URL.revokeObjectURL(url);

如果选择导出 JPEG,我会先铺一层白底(避免透明区域变成黑底/异常):

if (fmt === 'jpeg') {
  ctx.fillStyle = '#fff';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
}
ctx.drawImage(img, 0, 0);

批量 ZIP 下载则是把每张 blob 放进 zip:

const zip = new JSZip();
zip.file('a.webp', blobA);
zip.file('b.webp', blobB);
const out = await zip.generateAsync({ type: 'blob' });

适用场景

  • 需要把图片发微信/邮件,但体积太大
  • 网站上传/后台上传限制大小
  • 想做页面性能优化(图片体积直接影响首屏速度)
  • 处理包含敏感信息的图片,不希望上传到第三方服务器

后续计划

接下来我准备继续把图片工具做得更“顺手”,例如:

  • 更清晰的压缩参数建议(按分辨率/内容类型推荐质量)
  • 更多下载命名规则/批量重命名
  • 处理 EXIF 的“保留/删除”选项与压缩联动

如果你也经常用图片压缩,欢迎试用并给我反馈(我会优先补齐大家最常用的流程)。


相关链接