OutOfMemoryExceptions. If I try to process more than one or two images concurrently,
OutOfMemoryExceptions are inevitable. Since this code is going to be called from a servlet, I'm expecting to handle multiple concurrent requests.
This is not a new problem and people have been tackling it in various ways. Since I was working in a server environment and have control over what applications are installed, I decided to use ImageMagick for the image manipulation. ImageMagick is great; I've used it quite often in various shell scripts.
There's basically two ways to work with ImageMagick from Java. You can use JMagick, a JNI layer over the ImageMagick interface, or you can use Runtime.exec() to call the ImageMagick command line application. I opted for the latter as it seemed simpler when I pushed the code from my Mac to my Linux server.
Since finding and invoking ImageMagick's convert command can be somewhat problematic, I decided to write a simple fluent API in Java to hide the details. The result allows you to invoke convert using method chaining:
ImageMagick convert = new ImageMagick(Arrays.asList("/opt/local/bin"));
convert.option("-resize", "250x").run(new File("in.jpeg"), new File("out.jpeg"));
You create a new
ImageMagickobject. As a convenience, you can pass in a list of additional paths to check for the
convertcommand in the event that it isn't on the default path. If the
convertcommand can't be found, the constructor throws an
Once you have an
ImageMagickobject, you can execute
convertby chaining various method calls, ending in a
run()returns true if the command succeeds, false otherwise.
In less than 200 lines of Java code, I had a much nicer way to interact with ImageMagick. A fun experiment would be to take the code and implement an even nicer DSL in Groovy.
methodMissing()would allow fluent method chaining on steroids:
As Guillaume Laforge tweeted, using metaprogramming, categories, and syntactic sugar like named parameters you could end up with a full blown DSL that looks like this:
convert fuzz:15.pct, trim: true, in: file, out:file