IndexOutOfBounds using Javas imageio.write() to create byte array in png

目录


[工具类,做好异常log。能跑不代表就没问题!下面分享一个最近遇到的线上问题]

base64转PNG

下面是一个将图片的base64 字符串 转换成图片输出到客户端的工具类代码

   private void getImage(String base64Str, HttpServletResponse response) {
        byte[] base64 = Base64.decode(base64Str);
        ByteArrayInputStream arrayInputStream = null;
        try {
            arrayInputStream = new ByteArrayInputStream(base64);
            BufferedImage img = ImageIO.read(arrayInputStream);
            ImageIO.write(img, "png", response.getOutputStream());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

这个代码编译运行均没问题。可在线上环境运行1个月的时间,出现了3次error的情况。

案例分析

报错信息

java.lang.IndexOutOfBoundsException: null 
    at javax.imageio.stream.FileCacheImageOutputStream.seek(FileCacheImageOutputStream.java:170) 
    at javax.imageio.stream.FileCacheImageOutputStream.close(FileCacheImageOutputStream.java:231) 
    at javax.imageio.ImageIO.write(ImageIO.java:1580) 

最后找到了ORACLE的官网,看到了该Bug的描述。首先,该Bug是偶现的。其次该Bug主要是由于,flushedPos设置为缓存文件末尾的4字节,这会导致在刷新流时发生异常,因为“pos”是文件的实际结尾,并且位于“flushedPos”之前。

After some investigation, I've found that in javax.imageio.stream.FileCacheImageOutputStream.seek(), flushedPos is set to 4 bytes past the end of the cache file, which causes the exception when the stream is flushed since 'pos' is the actual end of the file, and is before the 'flushedPos'.

This bug does not always happen. In 20 draws it would occur anywhere from 3-7 times. (It's a mapping application, and the draws are map tiles.)

解决方法

接着按照jdk提供的替代方案解决,代码修改如下

  ImageIO.write(img, "png", new MemoryCacheImageOutputStream(response.getOutputStream()));

资料参考
[1] : https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6967419
[2] : https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8041746

上一篇:2021-06-06


下一篇:Java图片读取ImageIO.read()报错:Unsupported Image Type 解决方案