Convert BufferedImage into byte[] without I/O
Asked Answered
C

4

13

Hi I have a BufferedImage instance in memory and want to convert it into byte[] to encode as base64 string without I/O operation for performance consideration. I was using the following API:

ByteArrayOutputStream baos = new ByteArrayOutputStream ();
ImageIO.write(image,"png",baos);
return baos.toByteArray();

However, this API still implicitly writes the image to the OS temp directory, which will lead to failure in case that the underlying OS temp directory is full and the temp file cannot be created. Stack Trace:

Caused by: java.io.IOException: No space left on device
    at java.io.RandomAccessFile.write(RandomAccessFile.java:493)
    at javax.imageio.stream.FileCacheImageOutputStream.write(FileCacheImageOutputStream.java:134)
    at javax.imageio.stream.ImageOutputStreamImpl.write(ImageOutputStreamImpl.java:66)
    at com.sun.imageio.plugins.png.PNGImageWriter.write_magic(PNGImageWriter.java:376)
    at com.sun.imageio.plugins.png.PNGImageWriter.write(PNGImageWriter.java:1115)
    at javax.imageio.ImageWriter.write(ImageWriter.java:628)
    at javax.imageio.ImageIO.write(ImageIO.java:1480)
    at javax.imageio.ImageIO.write(ImageIO.java:1554)

Is there an efficient (like in-memory conversion or efficient I/O) way to do the conversion without I/O? Please advise.

Cipolin answered 19/4, 2012 at 19:51 Comment(6)
I am...surprised that this happens.Trivium
This is almost certainly a bug in the JVM. Have you tried the latest version?Pablo
@PeterLawrey: why do you consider this a bug? There is a whole class FileCacheImageOutputStream. However I wasn't aware of this mechanism in ImageIO at all.Ambala
IMHO, A disk cache shouldn't automagically write data until it fills your drive. When was the last time your browser crashed because it filled your drive. ;)Pablo
Is there a way that we can do this without using ImageIO? I'm having a similar problem but I would like to use an alternative because of performance reasonsConrad
The OS temp directory was filled by other application dump. hence the disk write error occures. I set the cache flag as false. However, ImageIO still takes fair amount of time. The invocation takes around 100ms for small images. Is there another efficient way to do this?Cipolin
S
12

Disable the ImageIO cache through the ImageIO.setUseCache() method:

ImageIO.setUseCache(false);

It is on by default according to the javadoc:

Sets a flag indicating whether a disk-based cache file should be used when creating ImageInputStreams and ImageOutputStreams.

When reading from a standard InputStream>, it may be necessary to save previously read information in a cache since the underlying stream does not allow data to be re-read. Similarly, when writing to a standard OutputStream, a cache may be used to allow a previously written value to be changed before flushing it to the final destination.

The cache may reside in main memory or on disk. Setting this flag to false disallows the use of disk for future streams, which may be advantageous when working with small images, as the overhead of creating and destroying files is removed.

On startup, the value is set to true.

Security answered 19/4, 2012 at 20:2 Comment(0)
W
5

Both mentions of ImageIO.setUseCache(false) is correct. However, if you don't like to disable disk caching for ImageIO globally, an alternative is to explicitly wrap the stream in a MemoryCacheImageOutputStream (which does in-memory caching instead of disk caching):

ByteArrayOutputStream baos = new ByteArrayOutputStream ();
ImageOutputStream stream = new MemoryCacheImageOutputStream(baos);
ImageIO.write(image, "png", stream);
stream.close();
return baos.toByteArray();
Wellrounded answered 13/12, 2013 at 16:18 Comment(3)
Seems, it doesn't true anymore, at least in 1.8. com.sun.imageio.spi.OutputStreamImageOutputStreamSpi#createOutputStreamInstance doesn't check the type of the stream but does check the useCache parameter. Anyway, it would be really nice to have a way to disable caching locally.Geithner
@Geithner The above solution does indeed provide a way for “locally” disabling the disk cache. When you create the MemoryCacheImageOutputStream directly like that you never hit the Spi, so whatever it does is irrelevant. PS: Note that ImageOutputSream is not a subclass of OutputStream, despite its name. And there’s a separate overload of ImageIO.write for this.Wellrounded
Indeed. Thank you very much!Geithner
H
3

ImageIO by default writes it's cache to disk even when you only use streams. Try disabling the cache with:

ImageIO.setUseCache(false);
Hunter answered 19/4, 2012 at 20:2 Comment(0)
S
2

((DataBufferByte)img.getRaster().getDataBuffer()).getData() automatically returns a byte array if your image was in a byte format. No need for any IO at all.

Sacrifice answered 14/12, 2012 at 23:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.