Java:读取图像并显示为ImageIcon

7
我正在编写一个应用程序,它将图像作为ImageIcons(在JLabel中)读取和显示,该应用程序需要能够支持jpeg和位图。
对于jpeg格式的图片,我发现直接将文件名传递给ImageIcon构造函数可以正常工作(即使显示两个大型jpeg),但是如果我使用ImageIO.read获取图像,然后将图像传递给ImageIcon构造函数,则在读取第二个图像时会出现OutOfMemoryError(Java Heap Space)(使用与之前相同的图像)。
对于位图,如果我尝试通过将文件名传递给ImageIcon来读取,那么什么也不会显示,但是通过使用ImageIO.read读取图像,然后在ImageIcon构造函数中使用此图像可以正常工作。
从阅读其他论坛帖子中了解到,两种方法在不同格式上不起作用的原因是由于Java与位图的兼容性问题,但是是否有一种方法可以解决我的问题,以便我可以在不出现OutOfMemoryError的情况下同时使用两种方法处理位图和jpeg?
(如果可能的话,我想避免增加堆大小!)
OutOfMemoryError由以下行触发:
img = getFileContentsAsImage(file); 

方法的定义如下:

public static BufferedImage getFileContentsAsImage(File file) throws FileNotFoundException { 
  BufferedImage img = null; 
  try { 
    ImageIO.setUseCache(false); 
    img = ImageIO.read(file); 
    img.flush(); 
  } catch (IOException ex) { 
    //log error 
  } 
return img; 
}

堆栈跟踪如下:
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
        at java.awt.image.DataBufferByte.<init>(DataBufferByte.java:58)
        at java.awt.image.ComponentSampleModel.createDataBuffer(ComponentSampleModel.java:397)
        at java.awt.image.Raster.createWritableRaster(Raster.java:938)
        at javax.imageio.ImageTypeSpecifier.createBufferedImage(ImageTypeSpecifier.java:1056)
        at javax.imageio.ImageReader.getDestination(ImageReader.java:2879)
        at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:925)
        at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:897)
        at javax.imageio.ImageIO.read(ImageIO.java:1422)
        at javax.imageio.ImageIO.read(ImageIO.java:1282)
        at framework.FileUtils.getFileContentsAsImage(FileUtils.java:33)

请发布一些触发OutOfMemoryError的示例代码。 - Thomas
3个回答

3
您的内存不足是因为ImageIO.read()返回的是一个未压缩的BufferedImage,它非常大,并且由于被ImageIcon引用而保留在堆中。然而,Toolkit.createImage返回的图像仍保持其压缩格式(使用私有的ByteArrayImageSource类)。
您无法使用Toolkit.createImage读取BMP(即使可以,它仍将保留在内存中未压缩,并且您可能会再次耗尽堆空间),但您可以读取未压缩的图像并以压缩形式保存在字节数组中,例如:
public static ImageIcon getPNGIconFromFile(File file) throws IOException {
    BufferedImage bitmap = ImageIO.read(file);
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    ImageIO.write(bitmap, "PNG", bytes);
    return new ImageIcon(bytes.toByteArray());
}

这样,未压缩的位图只有在加载或渲染时才需要在内存中保留。


太棒了!这个完美地运行了。只是出于好奇,为什么在使用位图文件时,这种方法只能通过使用“PNG”作为formatName才能工作? - 11helen
它需要是Toolkit可读的格式,并且需要有一个ImageIO插件。 GIF可能有效(但仅限于8bpp,因此质量可能会降低)。 JPEG也可以使用(但最适合用于摄影图像,这些图像可能已经是JPEG格式,因此会重复)。 - finnw

0

你试过这个吗?

ImageIcon im = new ImageIcon(Toolkit.getDefaultToolkit().createImage("filename"));

似乎无法处理位图,并且在两个大的JPEG图片上也失败了: “Image Fetcher 0”线程中的异常java.lang.OutOfMemoryError:Java堆空间 在java.awt.image.DataBufferInt.<init>(DataBufferInt.java:41)处 在java.awt.image.Raster.createPackedRaster(Raster.java:458)处 在java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1015)处 在sun.awt.image.ImageRepresentation.createBufferedImage(ImageRepresentation.java:230)处 在sun.awt.image.ImageRepresentation.setPixels(ImageRepresentation.java:470)处 ... - 11helen

0

难道你真的只是内存不足了吗?我的意思是,如果你使用 -Xmx1g 运行Java,错误是否仍然发生?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接