毕设做的是一个给社团用的活动相册站。后端是我自己写的单张图片超过 2MB 直接拒收。手机拍的原图动不动就有四五兆我就在前端加了一个上传前压缩的函数。第一版写得很简单质量从 0.92 开始每次减 0.05 重新编码第一次压到 2MB 以内就停。测了几张图都能正常传上去。我当时觉得这块做完了。后来准备秋招要把项目讲给面试官听我顺手在函数最后打印了一行「结果字节除以限制字节」。一张照片只用到了限额的 64.4%。一档一档往下调只用了64.4%额度测的时候我没拿社团的照片用的是一张系统自带的风景照3840×2160。机器是一台 16 GB 内存的 Apple M4 笔记本浏览器是 Chromium 149 的开源构建编码用的是浏览器原生的canvas.toBlob。JPEG 从 0.92 起步第一次编出来就已经不到 2MB 了。函数编了 1 次就停在 0.92 上只用了 64.4% 的额度。换成 WebP 更夸张同样编 1 次就停下只用了 48.9%。问题出在质量和体积的关系上。我原来以为两者差不多是一条斜着的直线。完全不是。这张照片导出 JPEG 时质量 0.90 是 1189 KB到 0.96 已经是 1981 KB。质量从 0.90 加到 1.00 只跨 10 档体积就涨了 4.8 倍从 0.30 到 0.80 跨了 50 档体积才涨 2.5 倍。曲线后半段陡得厉害在这一段每次减 0.05 步子就太大了。2MB 的限制正好落在 0.92 和 0.96 之间。逐档下调一步就跨了过去也不会回头往上找。剩下那几百 KB 的额度就这么浪费掉了。这张图是我复现页上的对照表。每一行是一个样本配一个目标体积。主要看填充率那一列结果字节除以限制字节。照片压到 2MB 以内的那两行里逐档下调的填充率明显掉了下去两种二分都在九成以上。编码次数也可以对着看逐档下调只编 1 次是因为停得早不是它更聪明。截图是跑一次截的耗时和正文里的数会差一点字节数不会变。改成整数二分后用到96.7%额度第二版我改成在 30 到 100 的整数质量里二分。这 71 个数最多编 7 次就能定下来。照片压到 2MB 以内时 JPEG 一共编了 6 次、用了 189 ms最后停在质量 0.96用到了 96.7% 的额度。压成 WebP 编了 7 次、用了 3.63 s最后停在质量 0.99 时用到了 93.0%。另外两类样本也一样往上走。程序生成的 24MP 类照片压到 2MB 以内时 JPEG 的填充率从 90.4% 升到 97.9%WebP 从 74.2% 升到 95.8%。图映说明页的截图压到 200KB 以内时 WebP 的填充率从 77.7% 升到 97.8%。逐档下调也有不吃亏的时候。那张风景照压 WebP 到 200KB 以内时三种方法都用到了 99.2%。区别是逐档下调要从 0.92 一路减下去编 13 次、用了 4.31 s。整数二分只编 6 次、用 1.79 s 就到了。逐档下调什么时候碰巧落在好位置要看从 0.92 往下哪一档刚好卡在限制边上。我试着猜过几次也没找出规律。浮点二分我也顺手跑了。JPEG 用它拿到的文件和整数二分一模一样还多编了两次。整数二分的上限还要封在0.99二分唯一一次翻车是在限制给得很松的时候。把截图压成 WebP、限制 2MB 时整数二分一路往上走到了质量 1.00出来一个 753 KB 的文件。可质量填 0.995 时文件才 217 KB。原因是浏览器里 WebP 质量填到 1.0 会切成无损编码体积一下变成 3.5 倍。我现在把二分上限封在 0.99原图本来就小于限制的话就干脆不重新编码。改完之后我去看了现成的工具是怎么处理这件事的。对照时我用的是图映 ImgIng一个免费的在线图片处理工具2026-09-28 的线上版本读的是它主页面的源码。它的图片压缩里没有「压到多少 KB」这种输入框。界面上只有一个 30 到 100 的质量滑杆和一个按图片内容推荐的起点。照片推荐 JPG 80 和 WebP 75图形文字类推荐 JPG 88 和 WebP 84。源码注释里说这张表是按 SSIM 实测加肉眼对比标定过的。我的理解是推荐质量和压到指定大小管的不是一回事。推荐质量管看不看得出损失指定大小管后端收不收。我的相册站遇到的是后一种。还有一处我没想明白。照片压成 2MB 以内的 WebP 时浮点二分用到了 94.9% 的额度比整数二分多 1.9 个点代价是多编一次。这张照片编一次 WebP 的中位数是 321 ms。为这一点额度让用户多等三百毫秒值不值我还没想好。二分要成立还得有个前提质量越高文件越大。我测的 8 条曲线都是这样可我只测了这几张图也只用了浏览器自带的两个编码器。如果你的项目里也有一个上传前压缩的函数可以先在函数最后加一行打印结果字节除以限制字节。照片压出来要是用不到八成额度多半就是停得太早了。换成整数二分再把上限设成 0.99 就行。