图片格式转换别再上传了:30 张 PNG 转 WebP,体积耗时全实测
30 张 PNG,18.91 MB,拖进去点一下,8.27 秒转完,输出 6.84 MB,体积少了 63.8%。
整个过程页面自己记的网络请求是 0 条,图片没有离开这台机器。
我把其中一张的输出从页面内存里捞出来,用 PIL 复核:文件头 RIFF/WEBP,1920×1080,171422 字节,源图 471842 字节。

在线案例与源码:https://atomgit.com/deli007/demo_park/tree/main/codearts-image-format-converter
成品只有一个index.html,转换在浏览器内存里用 canvas 完成,页面不负责上传。
一、为什么值得自己做:在线转换的第一步就是「上传」
搜图片格式转换,前几页的工具流程几乎一样:选文件、等进度条、下载。
进度条那几秒里,你的截图、证件照、客户给的素材,已经在别人的服务器上落过盘了。
我不介意图片被压缩,介意的是压缩之前先传一遍。
我要的只有一件事:文件在本机算完,页面不负责传。
二、准备环境:进入码道 Web,把需求写成一段话
浏览器打开 https://devcloud.cn-north-4.huaweicloud.com/chat/home ,登录后把需求写进对话框,就能拿到一个能跑的单文件应用。
不用装东西,不用配环境,本文这个工具就是这么来的。
码道有三种使用方式:WebUI(浏览器对话)、TUI(终端命令行)和桌面 IDE(IDE 插件)。本文用 WebUI 版演示。
需求我写了 10 条,事后看最值钱的是这四条:
- 页面上要有网络请求计数,从打开页面到转换结束,这个数必须一直是 0;
- 每张图要单独给出原体积、输出体积和减小百分比,不许只给一个总数;
- 遇到不支持的类型、损坏文件、空文件,要在那一行写清中文原因,不许只打控制台日志;
- 支持的输入格式写明 PNG / JPG / WebP / GIF / BMP,输出只留 WebP / PNG / JPEG 三种。
第二条是这次排版最省事的决定:逐张的数字摆在那里,写文章时不用去数。
三、一屏看清:能选什么,能看什么
页面分四块:选图片、输出设置、处理队列、汇总统计,最底下还挂着一块网络请求清单。

拖拽区标了五种支持的输入格式,点一下就能选文件,多选不限张数。
输出设置只有三个开关:格式(WebP / PNG / JPEG)、质量滑杆(1 到 100)、尺寸限制(勾选后最长边超过设定值就等比缩小)。
尺寸限制只缩小、不放大,所以小图勾不勾都一样。
选完文件按钮才会亮,队列空的时候「开始转换」是灰的。
四、实测:30 张 PNG 转 WebP,8.27 秒,少 63.8%
输入是我用 PIL 生成的 30 张噪点图,尺寸掺着来:1920×1080、1600×1200、1200×1600、800×600、3840×2160,合计 18.91 MB。
默认设置是 WebP、质量 80,点一下「开始转换」。

跑完汇总统计给出四个数:18.91 MB → 6.84 MB,减小 63.8%,总耗时 8.27 秒,平均每张 276 毫秒。
同一批文件我重复跑了三遍,耗时是 8.30、8.28、8.27 秒,稳定在 8.3 秒上下。
逐张看也有规律:1920×1080 那张从 460.8 KB 掉到 167.4 KB,3840×2160 那张从 1.75 MB 掉到 649.1 KB,减小幅度都在 63% 到 64% 之间。
噪点多的图最吃 WebP 的亏,也最能吃到它的便宜。
五、换个设置,数字差多少
同一批 30 张图,我换了六个设置各跑一遍。
| 输出设置 | 输出体积 | 体积变化 | 总耗时 | 平均每张 |
|---|---|---|---|---|
| WebP 质量 80 | 6.84 MB | -63.8% | 8.27 秒 | 276 毫秒 |
| WebP 质量 50 | 4.27 MB | -77.4% | 8.11 秒 | 270 毫秒 |
| WebP 质量 100 | 21.71 MB | +14.8% | 4.70 秒 | 157 毫秒 |
| WebP 质量 80 + 最长边 1280 | 3.59 MB | -81.0% | 4.43 秒 | 148 毫秒 |
| JPEG 质量 80 | 11.44 MB | -39.5% | 2.95 秒 | 98 毫秒 |
| PNG(无损) | 21.37 MB | +13.0% | 2.92 秒 | 97 毫秒 |
质量 100 的 WebP 比原图还大 14.8%,这一格值得单独说:canvas 的 WebP 编码器在质量拉满时不走有损模式,体积就放飞了。
PNG 输出同样比输入大 13%,因为我的输入图是 PIL 按默认压缩率写出来的,canvas 重编码一遍反而不如原文件紧凑。
所以「输出 PNG」这件事只适合本来就是 PNG 的图,别指望它顺手帮你瘦身。
真正把体积压下去的是两条路:质量降到 50 到 80,或者勾上尺寸限制。
第五行那组最能说明问题:最长边压到 1280,18.91 MB 直接变成 3.59 MB,少了 81%。
代价是分辨率,用之前想清楚这张图要拿去干嘛。
六、不上传这件事,我是怎么验的
页面底部那块清单,从打开页面开始记,凡是页面发出的请求都会逐条列出来,带地址和耗时。
30 张转完,清单是空的:请求 0、上传 0,绿条写着「检测到 0 次图片上传 / 数据外发请求」。

这个清单是页面自己记的,我不太想直接信,就从外面又对了一遍:performance.getEntriesByType(‘resource’) 在六个设置下都返回 0 条。
页面里也确实没有外部脚本和 CDN,整个 index.html 45066 字节,双击就能打开。
输出的东西我也验了一遍,不是只看页面报的数字。
单张那一路:从页面内存里把输出 blob 取出来,落盘后让 PIL 打开,读出来是 WEBP、1920×1080,文件头 RIFF + WEBP,171422 字节,和页面报的数一致。
打包那一路:把三张图的输出交给页面自带的 ZIP 写入器,拿到 879080 字节,用 Python 的 zipfile 读,三个条目是 photo-00.webp、photo-13.webp、photo-14.webp,逐条用 PIL 打开也都能解码。
顺带看到一个细节:ZIP 条目的压缩方式全是 0(存储),也就是打包不二次压缩,因为图片本身已经压过一遍,再压只会拖时间。
七、四种坏文件,页面各说各的
失败路径我专门造了四个文件:正常图片改名的文本、一个 SVG、一个 4096 字节的随机数据、一个 0 字节的空文件。

| 输入 | 页面给出的原因 |
|---|---|
| 预算表.txt | 不支持的文件类型「.txt」:仅支持 PNG / JPG / WebP / GIF / BMP,请移除该文件后重试 |
| vector.svg | 不支持的文件类型「.svg」:仅支持 PNG / JPG / WebP / GIF / BMP,请移除该文件后重试 |
| broken.png(4096 字节随机数据) | 图片数据损坏或编码异常,浏览器无法解码(可能是下载中断、转码失败或伪装的扩展名) |
| empty.png(0 字节) | 文件大小为 0 字节,属于空文件,无法解码 |
类型不对的文件在进入队列时就被挡下,不用等点转换。
损坏和空文件是解码阶段才发现的,原因分别落在「解码失败」和「大小为零」两条分支上。
只剩失败项时,「打包下载全部」保持禁用,它不会假装还有东西可下。
八、实测数据汇总
| 项目 | 实测值 | 说明 |
|---|---|---|
| 单个 HTML 文件体积 | 45066 字节 | 约 44 KB,无构建步骤,无外部依赖 |
| 输入规模 | 30 张 / 18.91 MB | 五种尺寸混排的噪点图 |
| WebP 质量 80 | 6.84 MB / 8.27 秒 | 减小 63.8%,平均每张 276 毫秒 |
| WebP 质量 50 | 4.27 MB | 减小 77.4% |
| WebP 质量 100 | 21.71 MB | 比原图大 14.8% |
| 最长边限制 1280 | 3.59 MB | 减小 81.0% |
| 单张输出复核 | 171422 字节 / WEBP | PIL 读出 1920×1080,源图 471842 字节 |
| 打包复核 | 879080 字节 / 3 个条目 | zipfile 校验通过,压缩方式为存储 |
| 页面资源请求 | 0 条 | 六个设置下都是 0 |
| 携带文件内容的请求 | 0 次 | 页面自记计数与资源列表都能对上 |
测试环境是 Chrome 155 内核的一台 Windows 机器,别的浏览器用的是各自的 canvas 编码器,输出体积会有出入。
九、本地复现
生成物只有一个 index.html,双击打开就能用,不需要联网。
也可以起个静态服务看:python -m http.server 8000,然后访问 http://localhost:8000 。
想自己造测试图,用 PIL 几行就能生成一批噪点图,我这轮的输入就是这么来的。
想看它有没有偷偷上传,按 F12 打开网络面板,再点一次转换,里面不会多出任何请求。
下载回来的成品想复核格式和尺寸,也交给 PIL:Image.open 读一遍 format 和 size,跟页面报的数对一下。
十、说点不足
GIF 只取第一帧,动图进去出来就是一张静图。
所有输出都走 canvas,所以原图的 EXIF 信息(拍摄时间、机型、GPS)在转换后全部丢失。
HEIC 和 AVIF 打不开,浏览器能解码的格式它才认。
文件多的时候是串行处理,30 张要 8 秒,300 张就得一分多钟,页面没有做并行。
还有一点得说明白:这是「转换」,不是「压缩工具箱」。它不做批量改名、不做水印、不做裁剪,这些得另找工具。
十一、用码道做这个小工具的体会
这次最值钱的一条需求是「页面上要有网络请求计数,从打开页面到转换结束必须一直是 0」。
这条需求把「不上传」从一句口号变成了一个能看的数字,验收时我只要盯住那块清单。
第二条心得是逐张给数字。汇总统计只有一个总数,写文章时说服力不够;逐张列出原体积和输出体积之后,规律自己就浮出来了。
第三条是失败路径要写在需求里。我写的是「坏文件要给逐条中文原因」,交回来就是四行各不相同的提示,验证时我照着表一条条对。
第四条是尺寸限制这类细节要说清「只缩不放」。需求里不写,实现时很容易顺手把 800×600 的小图放大到 1280,那是帮倒忙。
不足的地方也说一句:这个工具只覆盖格式转换和等比缩小,压缩比、水印、批量改名它一个都接不住。
- 点赞
- 收藏
- 关注作者
评论(0)