WebP图片批量转换:用Python和Pillow解决格式兼容难题
又写了个蛋疼的工具这次主角是 WebP 文件批量转换。先说这个工具的来历。前阵子我从网页上扒了一批素材图浏览器保存时全给存成了 .webp 后缀。WebP 在网页加载上确实香但现在回到了我的本地图片库问题一个接一个一部分看图软件能预览一部分直接秒退发给同事对方聊天工具弹一句“不支持的文件格式”要往某个旧版后台传图系统只认 JPG 和 PNG。在电脑前折腾了二十分钟我终于受不了了花了一晚上写了这个 WebP 批量转换工具。这个工具的思路很朴素输入一个目录把里面所有 .webp 文件批量转成 PNG 或 JPG支持递归子目录能保留透明通道也能处理动图 WebP原文件留着不动转换结果放到单独的输出目录。实现上用了 Python 加 Pillow总共一百多行代码不依赖任何重型环境。这篇文章不是单纯贴一段代码就交差而是把背后那堆折腾过程、技术选型、透明通道暗坑、动图 WebP 转 MP4 的扩展思路以及实操中踩过的问题全部复盘一遍。1. 都是被逼的WebP 这东西到底怎么回事1.1 先讲讲 WebP 是个啥WebP 是 Google 在 2010 年推出的一种图像格式设计目标很明确在同等视觉质量下把文件体积压得比 JPEG 更小。它采用的技术方案挺有意思是基于 VP8 视频编码里的帧内压缩技术来做静态图像压缩等于把视频压缩的那套思路搬到了图片上。除了有损压缩WebP 还支持无损压缩、Alpha 透明通道甚至支持动图一个格式把 JPEG、PNG、GIF 的活全揽了。压缩效果方面我拿自己那批图实测过同一张照片转成 WebP 有损格式文件大小普遍比同质量 JPEG 小 25% 到 35%。无损模式下对比 PNG 也有肉眼可见的体积优势。这也是为什么现在大量网站默认把图片输出成 WebP——服务器带宽省了用户加载速度快了移动端流量也省了。浏览器这边Chrome、Edge、Firefox 老早就支持了新版 Safari 也把支持补齐了所以你在浏览器上几乎感知不到它的存在。但问题恰恰出在这里网页端用得太顺本地生态却没完全跟上。WebP 在“传输”这个环节是王者到了“加工”和“存储”环节就变成了尴尬的存在。很多传统图像处理程序、老一点的设计软件、一部分看图工具对 WebP 的支持要么没有要么半残。再加上国内很多后台系统、CMS 编辑器、甚至即时通讯软件文件格式白名单里根本没有 webp 这一项导致你只要把 WebP 图片脱离浏览器环境就要开始跟各种“不支持”打交道。1.2 浏览器下载图片为什么会变成 .webp这一步很多人都有疑问。我在浏览器上右键保存图片明明页面里看着挺正常存下来怎么就是 .webp 了原理不复杂现在很多网站为了性能和带宽优化直接通过 HTML 的 picture 标签或者内容协商机制对支持 WebP 的浏览器输出 WebP 版本的图片。浏览器的“保存图片”功能拿到的是服务器实际返回的资源也就是说服务器给你的本来就是 .webp哪怕你视觉上看到的页面效果没区别。更隐蔽的是有些平台会在原图 URL 后面动态拼接压缩参数比如加一个“改为webp格式”的规则。这种情况下即便原始素材是 PNG服务器也预先转好了一个 WebP 版本给浏览器用。你保存到本地的自然就是 WebP。这不是浏览器故意的是服务器端做了格式切换。所以你会发现自己辛辛苦苦从网上找的参考图、素材图最后清一色全是.webp后缀根本绕不开。这种场景下如果你要拿这些图片做进一步处理比如放进设计稿、做成 PPT、上传到有格式限制的系统唯一的办法就是把它们转回常见的格式。单张转还好系统自带的照片应用、预览工具都能另存为可一旦是几十上百张手工一张张处理就纯属自杀式操作。写脚本批量转换就成了最理智的出路。1.3 现成工具那么多为什么非要自己写在动手写脚本之前我其实先试了一圈现成方案各有各的麻烦。在线转换网站是最常见的路子把图片传上去等它转好再打包下载。听起来操作成本很低但存在几个实际痛点。首先一次上传数量有限制免费站点通常限制在二十张以内其次涉及素材隐私问题图片是上传到别人服务器上的有些素材可能不适合往外传再次转换质量不稳定很多在线工具为了速度会粗暴压缩画质等你下载下来才发现细节已经糊成一片。几百张图靠网页传光上传下载和等待的时间就够喝一壶了。ImageMagick 命令行工具是另一个选项功能极其强大我用它转过不少图。但它最大的问题在于默认不带 libwebp 依赖的某些版本需要单独处理而且它的命令参数设计偏向于 Unix 老派风格一个批量递归转换的需求要用自己写 shell 循环还要处理目录结构、命名冲突、透明通道这些边界。每次要用都得翻一遍文档时间一长又忘属于“会用但不好用”的典型。其他现成的 GUI 转换工具我也看过几个要么免费版有图片数量限制要么格式支持不全要么常年不更新碰到新编码的 WebP 直接罢工。比起花时间研究这些工具的脾气自己用 Python 写个小脚本反而更可控代码逻辑完全在自己手里想加什么功能随时加换成什么环境跑都是一样的行为。本质上这个工具要解决的不仅是“转格式”这一个动作还包括批量、递归、保留透明通道、处理动图这些周边需求而这些恰好是通用工具最薄弱的地方。2. 方案选型为什么是 Python 加 Pillow2.1 这条路到底有什么好技术选型我基本没有犹豫直接选了 Python 加 Pillow 这个组合。理由很现实第一跨平台Windows 和 macOS 上跑法完全一样不用为不同系统准备不同版本第二Pillow 是图像处理领域最常用的库安装一个包就内置了 WebP 的编解码支持官方预编译的 wheel 包已经带了 libwebp用户不需要自己去编译任何东西第三Python 脚本天然适合这种“读文件、处理、写文件”的批处理需求代码短逻辑直白出问题也好调试。其实当时还考虑过其他技术栈。用 Node.js 加 Sharp 库也是个不错的选择性能好对于处理大量图片的场景比 Pillow 更有优势。但问题在于 Sharp 的安装依赖比较重构建需要下载原生模块在某些环境下装起来比想象中麻烦再者如果我临时在某个没有 Node 环境的机器上跑还得先装一套 Node成本就上去了。Go 语言编译成单个可执行文件的做法我也想过但写起来工作量会明显大于 Python而且这种小工具不值得我动 Go。选 Pillow 还有一个隐藏优势它对 WebP 的支持是渐进叠加的。新版 Pillow 对动态 WebP、透明通道的支持越来越好连 libwebp 的重灾问题也改善了不少。对于我需要的静态图转换、动图取帧、保留 Alpha 这些操作Pillow 都能用很简单的 API 完成。唯一要注意的是某些长期不做更新的旧版本 Pillow 对 WebP 支持不完整我后面在踩坑章节会专门讲。2.2 目标格式怎么选PNG 还是 JPG这是每个使用者都要面对的问题直接决定转换出来的文件能不能用。我的建议是分场景来。需要保留透明背景的图片比如 LOGO、图标、抠图素材转换成 PNG它是无损压缩还能保留 Alpha 通道适合做进一步编辑。不需要透明的照片、截图转成 JPG 更省空间兼容性也更好上传到各种系统基本畅通无阻。这里有个实践上的细节值得专门提醒WebP 图片本身带有透明通道的话直接转成 JPG 会翻车。JPEG 格式不支持 Alpha 通道Pillow 遇到 RGBA 模式的图转 JPG 会怎么处理呢通常会直接把 Alpha 通道丢弃而丢弃的后果往往是透明区域变成黑色底。我后面会讲怎么在代码里加一层“透明区域铺白底”的逻辑。如果只是随手转几张图不在乎这些小细节那直接选 PNG 当目标格式是最省心的。2.3 做成命令行工具而不是图形界面很多朋友拿到一堆图片第一反应是想要一个窗口界面的转换工具。我理解这种需求但在这个项目里我刻意没有做 GUI原因有几点。一是命令行工具写起来快调试方便。arparse 解析参数、pathlib 遍历目录、Pillow 做转换核心逻辑几十行搞定。换成 GUI光布局、事件绑定、文件选择窗口这些周边代码就得再翻一倍而且调试起来还要点来点去效率低。二是命令行天然适合批处理。它可以配合终端脚本、定时任务、持续集成流程使用比如每天定时把某个目录下的 WebP 转成 PNG一条命令加 cron/计划任务就能搞定。图形界面工具的自动化能力就差太多了。三是命令行工具更容易分享。代码复制给你装个 Python 环境运行命令就完事就算要加参数看一眼 help 就明白。GUI 工具要打成一个可执行文件分发还得考虑系统差异、签名、杀毒软件误报这些额外麻烦。当然也不是说 GUI 一无是处。如果后续这个工具要给不太懂命令行的同事用我也会考虑加一个简单的 tkinter 窗口这事后补其实不难。但在第一版里我选择把精力全部集中在转换逻辑本身。3. 核心代码到底怎么实现3.1 目录遍历与文件筛选第一步是找到所有需要转换的 WebP 文件。我用了 pathlib 库的 glob 和 rglob 方法比手写 os.walk 要简洁不少。需要说明的是rglob 会递归遍历所有子目录而 glob 不带星号递归时只扫描当前目录。如果你只需要处理一层目录用 glob(*.webp) 就够了素材库通常目录嵌套比较深我更推荐 rglob。文件后缀还有大小写问题。部分工具导出时会把后缀写成 .WEBP 或者 .Webp如果只匹配小写后缀就会漏文件。稳妥的做法是用 pathlib 读取每个文件的 suffix然后统一转小写再比较from pathlib import Path source_dir Path(./downloads) webp_files [p for p in source_dir.rglob(*) if p.suffix.lower() .webp]这样遍历出来的是一个 Path 对象列表后面无论是取文件名还是拼输出路径都很顺手。另一个需要考虑的问题是如果目录里有大量图片一次性把所有文件都收集到内存里其实没问题因为 Path 对象只存路径不装载图片数据真正吃内存的是后面 Image.open 的阶段。所以这个遍历逻辑对几百上千张图都不会有性能压力。3.2 单文件转换函数透明通道要单独处理转换的核心是一个单文件函数入参是源文件路径、输出目录和目标格式。我这里把透明通道的处理逻辑直接编进了函数里from PIL import Image from pathlib import Path def convert_single_file(src_path: Path, dst_dir: Path, target_format: str) - bool: 将单个 WebP 文件转换为指定格式返回是否成功。 try: with Image.open(src_path) as img: # 如果是动图 WebP取第一帧 if getattr(img, is_animated, False): img.seek(0) # 复制一份图像避免后续操作影响原图句柄 frame img.copy() # JPG 不支持透明通道这里把透明区域填充为白色 if target_format.lower() in (jpg, jpeg) and frame.mode in (RGBA, LA, P): rgba frame.convert(RGBA) background Image.new(RGB, rgba.size, (255, 255, 255)) background.paste(rgba, maskrgba.split()[-1]) frame background elif target_format.lower() in (jpg, jpeg): frame frame.convert(RGB) dst_path dst_dir / f{src_path.stem}.{target_format.lower()} frame.save(dst_path, formattarget_format.upper()) return True except Exception as e: print(f 转换失败 {src_path.name}: {e}) return False这里有三个关键点需要展开讲。第一个是img.seek(0)。Pillow 读取 WebP 时如果文件是动画 WebPPillow 会把它识别为多帧图像。直接用Image.open打开时默认读取第一帧但有些情况下图片对象的状态可能停留在最后一帧或中间帧。为了保证转换结果稳定我在操作前强制seek(0)回到第一帧。如果你想转换的是动图的每一帧那就需要后面的动图处理逻辑这个我会在第六部分展开。第二个是img.copy()。在 with 块中打开图像后文件句柄会被 with 管理如果我直接对 img 做 convert、save 操作处理过程中文件句柄可能被提前释放或影响后续帧操作。复制一份出来处理可以安全地避免这类诡异问题代价是多占一份内存但对于图片转换场景完全可接受。第三个是透明通道处理。JPG 没有 Alpha 通道概念如果直接拿 RGBA 模式的图像去保存 JPGPillow 一般会报错或者丢通道。我的处理方式是先把图像强制转成 RGBA然后新建一张纯白底 RGB 图再用原来的 Alpha 通道作为蒙版把透明区域粘贴上去。这样透明部分就会变成白色而不是默认的黑色视觉上更自然尤其是对透明底的图标和按钮图片来说这个处理非常关键。3.3 批量调度与命令行入口单文件转换函数写好后批量调度就很清晰了。整体逻辑是遍历收集到的文件列表逐个调用转换函数统计成功和失败数最后输出汇总信息。输出路径的目标目录用mkdir(parentsTrue, exist_okTrue)保证存在这样不会因为目录缺失而报错。命令行部分我用 argparse 实现设计了几个核心参数输入目录、输出目录、目标格式、递归开关、JPEG 质量。完整入口代码如下import argparse from pathlib import Path from PIL import Image def batch_convert(input_dir: Path, output_dir: Path, target_format: str, recursive: bool False, quality: int 95) - None: if not input_dir.is_dir(): print(f错误输入目录不存在 [{input_dir}]) return output_dir.mkdir(parentsTrue, exist_okTrue) if recursive: webp_files [p for p in input_dir.rglob(*) if p.suffix.lower() .webp] else: webp_files [p for p in input_dir.glob(*) if p.suffix.lower() .webp] total len(webp_files) if total 0: print(没有找到任何 .webp 文件。) return print(f发现 {total} 个 WebP 文件开始转换……) success_count 0 for index, src_path in enumerate(webp_files, 1): print(f [{index}/{total}] 正在处理: {src_path.name}) if convert_single_file(src_path, output_dir, target_format, quality): success_count 1 print(f转换完成成功 {success_count}/{total}) def main(): parser argparse.ArgumentParser(descriptionWebP 文件批量转换工具) parser.add_argument(-i, --input, requiredTrue, help输入目录路径) parser.add_argument(-o, --output, default./converted, help输出目录路径默认 ./converted) parser.add_argument(-f, --format, defaultpng, choices[png, jpg, jpeg, bmp, tiff], help目标格式默认 png) parser.add_argument(-r, --recursive, actionstore_true, help递归处理子目录) parser.add_argument(-q, --quality, typeint, default95, helpJPEG 输出质量1-100默认 95) args parser.parse_args() batch_convert( input_dirPath(args.input), output_dirPath(args.output), target_formatargs.format, recursiveargs.recursive, qualityargs.quality, ) if __name__ __main__: main()演示一下实际调用方式。把脚本存成webp_converter.py在终端里执行python webp_converter.py -i ./downloads -o ./converted -f png -r这段命令会把downloads目录及其所有子目录下的 .webp 文件批量转成 PNG输出到converted目录。如果不加-r则只处理当前一层目录。想把巨大的图片转成 JPG 并控制质量为 85 分只需要改参数python webp_converter.py -i ./photos -o ./jpg_output -f jpg -q 853.4 这一版做了什么取舍这个工具能解决大部分日常问题但我得诚实地列出它的不足。动图 WebP 的处理是取第一帧不是逐帧转换。对于需要把动图完整转成 GIF 或者 MP4 的场景我这版工具只处理了静态提取。如果你要的是完整动图就得用第六部分提到的动图扩展方案而不是直接依赖这个脚本。原文件名冲突。如果输入目录的多个子目录下存在同名文件而你又用了非递归扫描转换结果都输出到同一个目录后者会覆盖前者。这种情况我在写代码时没有做双保险你会丢失其中一个文件的内容。实际使用时建议先用-r看下输出目录里的文件情况或者干脆在代码里给同名文件加个序号后缀。EXIF 信息。WebP 文件里其实可以带上 EXIF 元数据但 Pillow 在读取 WebP 时对 EXIF 的支持比较有限转换后照片的拍摄参数不一定能完整保留。如果你的用途是做照片归档这点会影响后续检索和分类。这些都是取舍而不是 bug。工具的核心目标是把格式转对让图片能直接用起来其他细节我先不较劲。4. 跑起来直接从零开始实操一遍4.1 环境准备理论上只需要 Python 3.8 以上版本加 Pillow。在目录下手动创建虚拟环境再安装依赖是最清爽的做法不会污染全局环境python -m venv venv source venv/bin/activate # Windows 系统请用 venv\Scripts\activate pip install pillow装完之后建议先验证一下当前环境里的 Pillow 是否支持 WebP。某些精简版 Python 环境或者自己编译的 Pillow 可能没有带上 libwebp直接打开 WebP 会报错。验证代码也很简单from PIL import features print(features.check(webp))输出 True 说明 WebP 支持没问题输出 False 就需要升级 Pillow 到官方预编译版或者换一个 Python 环境。4.2 实测一批图片的转换效果我拿自己当时那批素材来做测试一共 128 个 WebP 文件大小从几 KB 的图标到几 MB 的 PC 壁纸都有。执行命令python webp_converter.py -i ./downloads -o ./converted -f png -r终端输出大概长这样发现 128 个 WebP 文件开始转换…… [1/128] 正在处理: banner_main.webp [2/128] 正在处理: icon_arrow.webp ... 转换完成成功 128/128单文件耗时基本看不出明显停顿。整批 128 张图片从开始到结束大概用了 8 秒多一点算下来每张平均几十毫秒。我看了看输出目录PNG 和 JPG 文件都正确生成透明图标的背景也保留了透明通道没有出现黑底问题。还顺手对比了一下文件大小一张 200KB 的 WebP 无损图转成 PNG 后变成 1.2MB符合预期PNG 的无损特性决定了它的体积就是比 WebP 大另一张 500KB 的 WebP 有损图转成 JPG质量 90后大概是 700KB。这说明 WebP 的压缩效率确实是实打实的从 WebP 转回 JPEG/PNG文件变大是正常的不要以为是代码出了问题。4.3 性能与内存注意事项批量转换的性能瓶颈通常不在 Python 代码本身而在两个方面一是 Pillow 的编解码速度二是磁盘 IO。如果你的图片分辨率特别大比如 8000 像素长边解码 WebP 时内存占用会明显上升因为 Pillow 会把整个图像位图loading 到内存。我从朋友的测试环境了解到一张超宽全景图分辨率接近 10000 像素在内存只有 8GB 的机器上转换时差点把内存打满。解决方案也很粗暴加一个限制当图片尺寸超过某个阈值时先用 thumbnail 缩略到合理尺寸再保存。另外我发现连续转换大量小图时磁盘写入反而是最明显的耗时点。如果你对速度敏感可以把输出目录放到固态硬盘上如果放到机械硬盘一批几百张图片的转换时间会明显拉长。当然这个也好理解图像转换本身是 CPU 密集加 IO 密集两头都得顾。5. 踩坑实录WebP 转换常见问题速查写工具不可避免地会遇到各种奇奇怪怪的问题我把这段时间踩过的坑汇总成表格同时挑几个重点展开。问题现象可能原因解决方案打开 WebP 报“cannot identify image file”Pillow 没编译 WebP 支持升级 Pillow检查 features.check(webp)透明图转 JPG 背景发黑JPEG 不支持 Alpha 通道直接丢弃转换前铺白底用 Alpha 做蒙版动图 WebP 只转出第一帧没有读取后续帧seek 到指定帧或使用动图扩展方案转换结果文件大小为 0目标目录不可写或磁盘满检查输出目录权限和磁盘空间文件名重复导致覆盖多个子目录下同名文件输出文件名加序号或按子目录分文件夹Pillow 报“image file is truncated”源 WebP 文件损坏用原始图片重新导出或跳过该文件转 JPG 报“cannot write mode RGBA”没有处理透明模式参考 3.2 节的颜色模式转换逻辑5.1 “cannot identify image file”日志这个报错是最让人困惑的一个因为明明同一张图在另一个环境里能正常打开。我排查了一圈结论就是 Pillow 的 WebP 支持没有编译进去。官方 PyPI 上发布的 Pillow wheel 一般自带 libwebp但某些平台或精简版 Python 环境可能用的是裸编译版不包含这个功能。遇到这种问题时先跑from PIL import features; print(features.check(webp))如果输出 False直接升级或重装 Pillow 就行不用怀疑代码逻辑。5.2 背景染黑的透明图把带 Alpha 的 PNG 转成 JPG结果透明部分变成黑色块这个问题在手工用 Photoshop 时也有。原理我之前讲过JPEG 格式完全没有 Alpha 通道的概念从 RGBA 转到 RGB多余的 Alpha 信息唯一的选择就是被丢弃而 Pillow 的默认丢弃策略是直接把透明部分当成黑色填充。解决方式只能在代码里显式处理先建白色底再贴着透明蒙版把图盖上。如果你的设计稿里透明部分将来可能是深色背景那这个白色底的填充策略也同样适用只是背景色可以从纯白替换成你需要的其他颜色。5.3 动图 WebP 转完变静态图这其实不是 bug而是工具定位。我的初版代码在检测到is_animated后强制取了第一帧所以动图转换出来只会有第一帧的画面。如果你需要动图的完整内容需要逐帧读取并保存成支持多帧的格式比如 GIF或者导出为视频。具体的逐帧读取思路我在第 6.2 节演示了代码。但为了让你少走弯路这里提前告诉你一个关键点Pillow 的img.seek(frame_index)在读取完最后一帧后再 seek 会抛出 EOFError你要用这个异常来控制循环结束而不是提前猜测总帧数。5.4 文件夹结构混乱和同名覆盖批量转换最容忽视的就是输出目录的文件名管理。我最初版本直接拿src_path.stem做输出文件名一旦输入目录的子目录里有重名文件转出来就会互相覆盖。怎么解决两个思路一是直接保留目录结构输出路径和输入路径保持相对关系二是简单点输出文件名后面加数字序号或者父目录名。考虑到大多数素材库的目录结构本身是有意义的我建议第一种思路转换结果按原有目录层级摆好后面整理素材时也清晰。6. 这个工具还能怎么折腾下去6.1 保留 EXIF 和拍摄信息WebP 格式本身支持嵌入 EXIF 元数据但对于从网页保存的图片来说能带上 EXIF 的情况比较少。如果你需要处理相机导出的 WebP 文件可以试试用 Piexif 库把原图的 EXIF 提取出来再写进转换后的图片里。思路是先用原图读取 EXIF 二进制数据保存时通过exif参数传给 Pillow。不过要注意Pillow 对不同格式的 EXIF 处理细节略有差异实际实现时记得验证一下转换后的字符编码与字段是否完好。6.2 动图 WebP 转 MP4 或 GIF动图 WebP 在网页上很常见比如一些微动效、表情包。你要是想把它们转成 GIF 或者 MP4 视频单靠 Pillow 不够需要配合 imageio 库。基本原理就是逐帧读取动图再逐帧写进目标容器。from PIL import Image import imageio.v2 as imageio import numpy as np frames [] with Image.open(animation.webp) as img: img.seek(0) while True: frames.append(np.array(img.convert(RGB))) try: img.seek(img.tell() 1) except EOFError: break # 保存为 MP4 with imageio.get_writer(output.mp4, fps12, codeclibx264) as writer: for frame in frames: writer.append_data(frame)运行这段代码前请先安装依赖pip install imageio imageio-ffmpeg。imageio-ffmpeg 会在第一次使用 MP4 写入时自动下载 ffmpeg 二进制如果你所在的网络环境对下载不太友好可能要提前处理好 ffmpeg 的安装。帧率建议设置为 12 到 15模拟原来的动图节奏具体数值要看你原始动图的时长和帧数来决定。GIF 的写法类似imageio.mimsave(output.gif, frames, fps12)一行就能搞定。这个扩展做完这个工具就能顺手覆盖视频小能手的一部分场景了比如把手头的动图素材统一转成项目需要的视频格式。6.3 加一个简单的图形界面上面说我不想做 GUI但如果你的使用场景是给小白同事提供工具那还是得有个窗口。tinker 的filedialog选目录、一个格式单选、一个转换按钮核心逻辑直接复用已有的batch_convert函数界面部分几十行就写完。我建议这种做法界面和核心逻辑解耦界面代码单独放一个文件核心转换模块留成纯 Python 函数将来想接别的入口比如 Web API、定时任务都只需要重新写一层壳。6.4 反向操作把图片压成 WebP工具写多了总会有反向需求浮现出来。既然 WebP 格式压缩效率高那网站开发者正好需要把 PNG、JPG 图片批量转成 WebP 来加速页面加载。转换代码更简单Pillow 的 save 函数直接支持 WebP 格式from PIL import Image img Image.open(photo.png) img.save(photo.webp, WEBP, quality80, method6)quality控制有损压缩质量method控制压缩算法耗时和效果从 0 到 6 越大越慢但文件越小。如果你有几十张小图跑一遍这个脚本能把图片体积平均缩小三成左右直接部署到网站 CDN 或者静态资源目录效果立竿见影。这个扩展方向和本文的转换工具正好互补一个负责进来一个负责出去以后维护素材库的流程就完整了。写这个工具最大的收获除了搞定那一批图片反而是把 WebP 这个格式的脾气摸透了。很多网上查到的资料只说它好、快、省流量但实际用起来的各种边缘情况比如透明通道、动图帧、EXIF、编译依赖不亲自踩一遍真不会长记性。我个人建议你如果也打算复制这套代码自己改着用先小批量跑一次确认输出格式和预期一致再上全量目录。等到转换流程用顺了还可以把输出目录接到自己的素材管理工作流里让这批图片转换工具成为整个流程中的一个小环节。工具是死的流程是活的说不准下次又有别的格式来逼你再蛋疼一把。