Javas ImageIO works… well, most of the time. It contains some unfixed, jpeg related bugs, but it works.
It may contain some dangers when used in a webbased application for generation large images on the fly.
Most problems are related to ImageIOs filed based caching and not flushing buffers when an IOException in an underlying stream occurs.
First, the javax.imageio.ImageReader
. It caches some data in files. It is essential to dispose the reader and the underlying ImageInputStream after use if it’s not needed anymore like so:
if(imageReader.getInput() != null && imageReader.getInput() instanceof ImageInputStream)
((ImageInputStream)imageReader.getInput()).close();
imageReader.dispose(); |
if(imageReader.getInput() != null && imageReader.getInput() instanceof ImageInputStream)
((ImageInputStream)imageReader.getInput()).close();
imageReader.dispose();
If it isn’t closed and disposed, the temporary cache files are either deleted not at all or maybe at the next garbage collection. I was able to bring down a VM by “java.io.FileNotFoundException: (Too many open files)” several times because i didn’t close a reader in a loop. Even the classloader wasn’t able to load any new classes after the ImageReader going mad on the file handle.
The other side is the javax.imageio.ImageWriter
. There is an issue mentioned in the Tomcat Wiki.
I used the ImageWriter to dynamically create large images. I directly passed the javax.servlet.ServletOutputStream
to the ImageWriter. If the generation of images takes long enough, there’s a good chance that the client aborts the request and the ServletOutputStream gets flushed right when the ImageWriter is writing to it. I didn’t have the exceptions mentioned in the wiki but my VM crashed. Great. I guess it had something to do with the native org.apache.coyote.ajp.AjpAprProtocol
Ajp Apr connector i use, but that’s just guessing.
I solved this problem by using a temporary file and its related outputstream which i then stream like described here. This solution is not only faster but i also can catch any exception related to an aborting client.
Also take care to dispose the write as well like so:
try {
imageWriter.setOutput(out);
imageWriter.write(null, new IIOImage(image, null, null), iwp);
out.flush();
} catch(IOException e) {
imageWriter.abort();
throw e;
} finally {
try {
out.close();
} catch(Exception inner) {
}
imageWriter.dispose();
} |
try {
imageWriter.setOutput(out);
imageWriter.write(null, new IIOImage(image, null, null), iwp);
out.flush();
} catch(IOException e) {
imageWriter.abort();
throw e;
} finally {
try {
out.close();
} catch(Exception inner) {
}
imageWriter.dispose();
}
This took me several hours to figure out… I hope someone finds this post useful.
Filed in English posts, Java
|