如何检查图像是否太大而无法打开?

How to check if the image is too large to open it?

如果指定的图像文件包含的图像太大,就加载它所需的内存而言,会引发 OutOfMemoryError。出于同样的原因,如果缩放操作的比例因子会创建一个太大的图像,OutOfMemoryError 如下所示:

Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
    at java.awt.image.DataBufferInt.<init>(DataBufferInt.java:75)
    at java.awt.image.Raster.createPackedRaster(Raster.java:467)
    at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1032)
    at java.awt.image.BufferedImage.<init>(BufferedImage.java:340)
    at java.awt.image.AffineTransformOp.createCompatibleDestImage(AffineTransformOp.java:461)
    at java.awt.image.AffineTransformOp.filter(AffineTransformOp.java:226)

是否可以通过执行一些初步检查来防止此错误的发生? 例如,如果应用程序根据可用内存量确定可以加载到内存中的最大图像大小(以像素为单位),则可以避免执行缩放,其结果图像的像素数将大于确定的限制。

如何处理从文件中读取图像的上述问题?如何在不将图像文件加载到内存的情况下获取图像文件的大小?这个问题可以使用 this Q&A 中提出的解决方案来解决:这个解决方案非常快,因为图像大小是从文件中读取的,而不是加载整个图像。

总结...

第一个目标是执行初步检查以获得要显示的图像的大小,而不加载整个图像:

在这两种情况下,您都会得到应加载到内存中的图像大小,即像素总数。该值可以与值限制进行比较。但是,如何计算这个限值呢?如何根据可用内存量确定可加载到内存中的最大图像大小或最大像素数?

您似乎已经发现,通过查看链接的答案和评论,您可以获得给定格式的 ImageReader,然后向 reader 询问最终尺寸图片。但是,即使 VM 有足够的可用内存,请记住 BufferedImage 需要 连续的内存块 ,并且可用的最大块可能比可用内存,由于碎片。据我所知,没有 API 可以从 VM 中找到 "largest continuous block of memory" 的大小。

可以在尝试解码之前预先创建图像,方法是使用如下代码(为便于阅读而简化):

ImageReader reader; // the reader for your format
reader.setInput(input); // your input

// Create image from type specifier
ImageTypeSpecifier spec = reader.getImageTypes(0).next();
BufferedImage destination = spec.createBufferedImage(reader.getWidth(), reader.getHeight();

ImageReadParam param = reader.getDefaultReadParam();
param.setDestination(destination);

reader.read(0, param);  // Image will be decoded to destination

但是,请记住,解码过程可能需要一些额外的内存用于数据结构、缓冲区等,以有效地解码图像。因此,即使您知道尺寸,甚至在内存中有目标图像,仍然 有可能在解码时 运行 内存不足。

(预先创建目标图像和进程使用的内存也适用于 AffineTransformOp 或任何 BufferedImageOp,真的)。

所以,我想我会支持@skykings 的建议,你最终应该以某种方式处理 OutOfMemoryError。几乎不可能保证您有足够的内存来预先解码给定的图像。