📌 摘要
在做 Markdown 转 PDF/Word 的过程中,我以为图片处理只是个小问题,结果却接连踩坑:有些图片扩展名是 .png,但内容却是 WebP,导致 Java 自带的 ImageIO 无法识别。经过一番摸索,我最终通过引入 imageio-webp 扩展,顺利实现了图片的 转码与批量处理。这篇文章会聊聊:
- WebP 格式的特点与优势
- Java 原生 ImageIO 的局限
- 如何用
imageio-webp支持 WebP 导入/导出 - 批量处理 Markdown 图片并转存为 WebP 的完整示例
- 以及 WebP 与 JPEG/PNG/GIF 的对比
希望能帮到在 Java 项目里遇到类似问题的同学 🚀。
WebP 是什么?
WebP 是 Google 推出的新一代图像格式,目标是替代传统的 JPEG / PNG / GIF。它有几个显著特性:
高压缩率:相同画质下,比 JPEG 小 25%~35%,比 PNG 小 26%~34%。
透明支持:有损/无损模式都能支持 Alpha 通道。
动画支持:可以替代 GIF,更小体积更高质量。
浏览器兼容性好:主流浏览器(Chrome、Firefox、Edge、Safari)已全面支持。
这也是为什么很多网站(比如简书、微信公众号)都会把上传的图片自动转成 WebP,从而提升页面加载速度。
我遇到的问题
我的任务是:把 Markdown 里的远程图片下载到本地,并统一转成标准格式(方便 PDF/Word 渲染)。
原始实现大概是这样:
BufferedImage image = ImageIO.read(new URL(originalUrl));
if (image == null) {
throw new IOException("无法识别图片格式: " + originalUrl);
}
ImageIO.write(image, "png", new File("output.png"));
结果在处理简书的图片链接时,日志里不断报:
Unrecognized Image format
ImageIO.read 返回 null
原因很简单:Java 自带的 ImageIO 并不支持 WebP。
解决方案:引入 imageio-webp
好在社区已经有现成的扩展 —— TwelveMonkeys ImageIO 提供了 imageio-webp 模块,直接给 Java 的 ImageIO 增加了 WebP 编码/解码能力。
在 Maven 里加上依赖:
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-webp</artifactId>
<version>3.12.0</version>
</dependency>
示例代码:PNG → WebP
有了扩展库之后,ImageIO 就能直接读写 WebP 了。
BufferedImage image = ImageIO.read(new File("input.png"));
// 导出为 WebP
File output = new File("output.webp");
boolean ok = ImageIO.write(image, "webp", output);
System.out.println(ok ? "导出成功" : "导出失败");
如果还想控制质量,可以用 ImageWriteParam:
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("webp");
ImageWriter writer = writers.next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.8f); // 0~1,越大画质越好
try (ImageOutputStream ios = ImageIO.createImageOutputStream(new File("output_q80.webp"))) {
writer.setOutput(ios);
writer.write(null, new IIOImage(image, null, null), param);
}
writer.dispose();
批量处理 Markdown 图片 → WebP
在实际项目里,Markdown 里可能有很多远程图片:


目标:把这些图片全部下载到本地,并统一转存成 WebP,Markdown 链接也改成本地路径。
实现思路
用正则扫描 Markdown 图片链接;
下载远程图片 → 用
ImageIO转成 WebP;存储到按日期划分的目录里(如
/images/2025/09/24/xxx.webp);替换 Markdown 内容中的 URL。
代码示例
Matcher matcher = IMAGE_PATTERN.matcher(markdown);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String originalUrl = matcher.group(1);
// 已经是本地路径,跳过
if (originalUrl.contains("/images/")) {
matcher.appendReplacement(sb, matcher.group(0));
continue;
}
String fileName = Paths.get(new URL(originalUrl).getPath()).getFileName().toString();
String baseName = com.google.common.io.Files.getNameWithoutExtension(fileName);
// 日期目录
String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
Path targetDir = Paths.get(tempImagePath, datePath);
Files.createDirectories(targetDir);
Path fixedTarget = targetDir.resolve(baseName + ".webp");
// 下载并转存为 WebP
BufferedImage image = ImageIO.read(new URL(originalUrl));
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("webp");
ImageWriter writer = writers.next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.8f);
try (ImageOutputStream ios = ImageIO.createImageOutputStream(fixedTarget.toFile())) {
writer.setOutput(ios);
writer.write(null, new IIOImage(image, null, null), param);
}
writer.dispose();
// 替换 Markdown 链接
String localUrl = "/images/" + datePath + "/" + fixedTarget.getFileName();
matcher.appendReplacement(sb, " + ")");
}
matcher.appendTail(sb);
String newMarkdown = sb.toString();
运行后,所有远程图片会被下载 → 转成 WebP → 存在本地 → Markdown 链接同步更新。
常见图片格式对比
| 特性 | JPEG | PNG | GIF | WebP |
|---|---|---|---|---|
| 压缩方式 | 有损 | 无损 | 无损(基于调色板) | 有损 + 无损 |
| 体积大小 | 小(但画质会损失) | 大(比 JPEG 大很多) | 很大 | 更小(比 JPEG/PNG 小 25%~35%) |
| 透明通道 | ❌ 不支持 | ✅ 支持 | ✅ 支持(1 位透明度) | ✅ 支持(8 位 Alpha) |
| 动画支持 | ❌ 不支持 | ❌ 不支持 | ✅ 支持 | ✅ 支持(比 GIF 更小更清晰) |
| 色彩深度 | 24 位真彩色 | 24 位 + 8 位透明 | 8 位(最多 256 色) | 24 位 + 8 位透明 |
| 浏览器支持 | 全部支持 | 全部支持 | 全部支持 | ✅ 主流浏览器已全面支持 |
| 适用场景 | 照片、网页图像 | 需要透明、无损的图标 | 简单动画、小图标 | 通用:照片、透明、图标、动画均可 |
总结
WebP 已经是互联网图片的新常态,兼顾小体积和高画质。
在 Java 中,需要引入
imageio-webp扩展才能支持读写 WebP。实际项目中,可以用正则扫描 Markdown,把远程图片统一转存为 WebP,既优化了文档体积,也提升了兼容性。
图片这种“小问题”,往往决定了文档导出的体验好坏。提前踩坑并解决,后面就能一路顺畅 🚀。