解决ImageIO无法读取部分JPEG格式图片问题
问题描述
我最近对在线聊天功能进行了一些内存优化,结果在回归测试时,突然发现有张图片总是发送失败。测试同事把问题转到我这儿来看,我仔细检查了一下,发现是上传文件的接口报错,说文件格式不符合要求。乍一看这问题似乎很简单,文件格式限制而已。我心想:JPEG 格式不应该是允许的么?然而,当我右键查看文件属性,心里瞬间有种不好的预感——这张图片确实是 JPEG 格式的,而系统明明允许上传 JPEG 类型的文件。这就变得有趣了。
问题排查
首先,我查看了测试环境的日志,却没有任何有用的线索。然后打开上传服务的代码,检查文件处理的逻辑,看起来一切正常,没有明显问题。既然看不出来问题,那就只能踏踏实实地 debug 了。
服务启动后,我用另一张 JPEG 图片测试了一下,结果上传成功了。这让我怀疑测试环境的代码和我们本地的版本有差异。于是,我再次使用之前总是上传失败的图片,测试后同样报了和测试环境一样的错误。我将断点打在文件校验的部分,逐行调试,结果发现所有的校验步骤都通过了!看来问题不在文件类型校验上。继续深入调试,发现流程走到了文件为空的逻辑,然后直接返回了“不允许上传该文件类型”的错误。这让我摸不着头脑——文件明明有大小,而且后端也没有报错,怎么会读取不到文件内容?
接着,我看了一下获取图片内容的代码:
BufferedImage image = ImageIO.read(fileData.getInputStream());
看起来平平无奇,但是这个image
对象返回来的确实是null
。
问题原因
经过深入分析,终于找到了问题的根源:ImageIO.read()
方法在 Java 中并不支持 .webp
格式图片的读取。这是因为 ImageIO
类依赖的 SPI(Service Provider Interface)
机制,只为常见的图片格式(如 PNG
、JPEG
、GIF
等)注册了相应的读取器,但并没有对较新的 WEBP 格式提供内置支持。
当我们尝试用记事本打开那张图片时,发现它的确是 WEBP
格式,而不是我们最初以为的 JPEG
。这就解释了为什么系统上传失败——ImageIO.read()
方法无法处理 WEBP
格式的文件,导致图片无法被正确读取,进而触发了文件格式不符合要求的错误。
有问题的JEPG
图片数据:
正常的JEPG
图片数据:
问题修复
解决这个问题也非常简单,直接在pom文件引入如下组件:
<dependency>
<groupId>org.sejda.imageio</groupId>
<artifactId>webp-imageio</artifactId>
<version>0.1.6</version>
</dependency>
然后就可以了!!!