作品 / 图片工坊 · 离线批量图片处理
桌面工具 · DESKTOP2026NO.20个人项目

图片工坊 · 离线批量图片处理

给「不能联网的电脑」做的批量压图工具:拖进文件夹,压到指定体积、限住宽高、转格式,PDF 直接拖进去就能压——全程不上传、不联网、不碰原图

GoWails v2Vue3PiniaSQLite
图片工坊 · 离线批量图片处理 界面截图
1 / 4
批量处理与汇总

缘起

它起于一个很具体的观察:内网办公的人压图,靠的是系统自带的「画图」——缩到 50% 再另存,压完要么糊了、要么还是超。

这种电脑上,装软件要审批,文件出不去,在线压缩网站第一步就打不开。而真实需求非常具体:系统只收 5MB 的附件,扫描件一动 40MB。市面上没有一个现成工具是为这个环境做的。

所以做的东西也很具体:一个装进去就能用、全程不联网、把原图保护好的批量压图工具。

背景

目标用户不是设计师也不是程序员,是单位里要交材料的人。这决定了两件事:

参数得替用户做掉。「质量 75」「DPI」「色彩空间」这些词对目标用户没有意义,他们心里只有一个数——系统限的 5MB。所以产品形态是「填一个想要的体积,工具自己想办法压到那个数」,中间的工程决策(二分找最高达标质量、PNG 没有质量旋钮只能缩尺寸、压得太狠要提醒)全部沉到实现里。

**信任要靠如实报告攒。**内网装一个软件有成本,装完发现「悄悄把图压糊了」就再也不用了。所以每一张图发生了什么都有交代:重新保存了几张、哪几张压到了屏幕清晰度、哪几张不支持、哪几张本来就很省空间——不支持的就明说不支持,不悄悄跳过。

方案

**三条图片规则,选着用。**压到指定体积、限制宽高上限、转成哪种格式——三条可以单用也可以同用;体积与尺寸冲突时体积优先。文件夹直接拖进去,递归扫描、按路径去重,默认双 worker 并行。

**PDF 免规则,拖进去就压。**内嵌 JPEG 按质量 75 重存、满页扫描图压到 150 DPI 屏幕清晰度、再做无损结构优化;文字和矢量内容字节级不动。加密的、带签名的直接拒绝——改了签名就失效,这种忙不能帮。这套纯 Go 实现的完整决策过程写在了博客长文《不引 Ghostscript:我用纯 Go 给 PDF 做了一场「压缩手术」里。

**原图零写入。**处理结果先进应用内暂存区,原图所在的文件夹一个字节都不动;点「下载」才保存到用户选的文件夹,先写临时文件再原子落位,失败不留半成品。这是第一版直接写回原图旁边、被真实使用者纠正之后改的——隐性约束不是一次想全的,是踩出来的。

**界面说人话。**用户可见文案有一张术语表当单一真相:「压到了屏幕清晰度」而不是「降采样」、「不支持的图片类型」而不是「JBIG2/CCITT」、已经满足设置的图叫「无需处理」而不是「已达标跳过」。Go 侧返回的提示与前端界面同表取词,改词先改表。

技术架构

模块技术选型
桌面运行时Wails v2(Go 后端 + Vue3 前端),hash 路由适配打包形态
后端Go 1.26 + GORM + SQLite(纯 Go 驱动,无 CGO)
图像处理标准库 + golang.org/x/image,目标体积两层搜索(二分质量 + 平方根缩尺寸)
PDF 压缩pdfcpu 做解析回写底座,图像重压缩对象级自研(质量 75、仅更小才替换、150 DPI 降采样)
工程化Makefile 统一入口 + 架构护栏 + 依赖登记制 + OpenSpec 变更治理
交付macOS dmg / Windows NSIS / 麒麟 deb,CI 三平台打包并附 SHA256SUMS

几个值得说的做法:

  • 编解码全纯 Go:不引任何需要外部编译器的图像依赖,同一套代码交叉出三平台(含信创 arm64)安装包,麒麟构建钉死 Ubuntu 20.04 容器对齐 glibc 2.31。
  • 约定编译成会红的检查:仓库里有 1802 行护栏测试——前后端契约常量双向核对、日志/事件/版本注入这类「漏了不报错」的接线逐条验证、依赖必须登记、分层纪律强制。任何靠解析源码做断言的护栏,解析到 0 个目标必须报错,防止护栏自己失效了还亮绿灯。
  • 版本号四处同源:wails.json 是唯一真相,构建时渲染进安装包与二进制,护栏核对一致性。
  • 图标资产单源派生:build/*.svg 是唯一源,make icon 派生全部平台图标并登记哈希,派生物不许手改。

成果

  • 已发版:v0.2.0 打 tag 自动构建三平台安装包与校验和,GitHub Release 可下载;从第一个提交到交付 0.1.0 用了两周,PDF 压缩作为 0.2.0 的主体功能补齐。
  • 质量可数:功能代码约 4400 行,测试与护栏约 6200 行(其中护栏 1802 行);10 份架构决策记录;PDF 压缩 spike 实测整文件 -42.9%、渲染像素差 0.41/765(肉眼不可辨)。
  • 自用与家人使用中:家里交材料的活儿现在都走它。没有对外推广,没有用户数可以报,这一栏不填好看的假数字。

体验

源码在 github.com/Benny66/file-processor,Release 页有三平台安装包;没有网页版 demo,桌面工具想用起来就是装一个。

想验两件事的话:

  1. 拖一个 300 DPI 的扫描件 PDF 进去,点开始。结果消息会写「重新保存了 x 张图片,其中 y 张压到了屏幕清晰度」——体积通常能掉一个数量级。
  2. 把压好的文件再拖进去跑一遍。这一遍会全部显示「已经很省空间」,一个字节都不会再动——逐张「仅更小才替换」的意义就在这。
MANUAL · 说明书

它到底能做什么、怎么跑起来

散文讲的是「为什么做」,这一节讲的是「它到底是什么」——可直接对照评估。

01 / 规格
交付形态
一个安装包装进系统就能用(macOS dmg / Windows NSIS / 银河麒麟 deb),不需要另装运行环境(Windows 需系统自带或随装 WebView2)
运行要求
macOS 10.13 及以上(Universal 二进制);Windows 10/11;银河麒麟 V10 SP1(2203,glibc 2.31)
输入支持
JPEG、PNG、WebP、BMP、TIFF、静态 GIF 与 PDF(只压缩、输出仍是 PDF);单文件上限 512 MB、单图上限一亿像素
隐私边界
全程离线——不上传、不联网、无遥测;输出是全新编码,GPS 与拍摄信息一并剥离
数据存放
常用设置与最近 30 次历史存本机 SQLite;没有账号、没有云同步
适用规模
面向个人与内网办公的单机使用,默认 2 个 worker 并行
02 / 功能清单
  • 批量导入文件与文件夹直接拖入,递归扫描、按路径去重;处理中界面不卡,单张失败不影响整批
  • 格式转换JPEG / PNG / WebP 互转,「自动」档逐格式竞速取最小;格式以内容探测为准,扩展名骗不了它
  • 目标体积二分搜索能达标的最高质量,质量到底仍超标再按比例缩尺寸,留 5% 余量防四舍五入反弹
  • 宽高上限盒等比缩放进盒,不裁切、不拉伸、不放大;与目标体积冲突时体积优先
  • PDF 压缩内嵌 JPEG 按质量 75 重存(仅更小才替换)、满页扫描图压到 150 DPI 屏幕清晰度、无损结构优化;文字一字不动
  • 暂存与下载结果先进应用内暂存区,原图所在文件夹零写入;点「下载」才落盘,先写临时文件再原子落位,失败不留半成品
  • 历史记录最近 30 次处理可回看——什么时候、用了什么设置、每张结果如何,没下载的随时再保存
  • 常用设置参数起名存档一键套用,应用重启恢复上次使用的设置
03 / 部署与运行
  • 怎么装:GitHub Release 下载对应平台安装包;个人分发未做签名,首次打开会被系统拦一次,按 README 步骤放行即可(每台机器一次)
  • 怎么核对:安装包经网盘或聊天工具转发后,用 Release 附带的 SHA256SUMS.txt 校验和核对
  • 怎么维护:设置与历史存在系统应用数据目录;从源码构建走 make build / test / lint,打包走 make package
04 / 已知限制
  • 不支持 AVIF / HEIC 与动图:避免三平台系统编解码依赖;iPhone 默认存的 HEIC 需先转 JPEG
  • 扫描页压到屏幕清晰度:满页扫描图默认 150 DPI,屏幕阅读够用、打印可能不够;非满页插图不缩像素
  • 部分 PDF 图片不重存:传真码(JBIG2/CCITT)、Flate 截图、CMYK/索引色保持原样并如实计数;加密与带签名的 PDF 直接拒绝处理
  • 不保留 ICC 色彩配置:广色域图片可能轻微偏色,交材料够用、印刷不建议
  • 未做代码签名与公证:个人分发不办证书,他人首次打开会被 Gatekeeper / SmartScreen 拦一次
  • 麒麟与信创 arm64 尚未真机验收:deb 由 CI 产出并在容器内验证安装,真机全流程未跑

有类似的事要管?

说说现在是怎么做的——Excel、微信群、纸质单子都行。我判断能不能做、大概怎么做。

聊聊你的需求