1

What is the most efficient way of reading in lots of images in CF / Railo and checking their width and height?

In my app, I need to typically read in about 20 images + and at the moment this takes up to 14 seconds to complete. A bit too long really.

theImageRead = ImageNew(theImageSrc);
if ( imageGetWidth(theImageRead) > 100 ) {
    writeOutput('<img src="' & theImageSrc & '" />');
}

Images are read from a list of absolute URL's. I need to get images specified over a certain dimension.

If there's a quicker solution to this then I'd love to get your insight. Perhaps underlying java methods?

I am also using jSoup if there's anything in that which could help.

Thanks, Michael.

Michael Giovanni Pumo
  • 14,338
  • 18
  • 91
  • 140

2 Answers2

5

I don't believe there's any way to determine the pixel dimensions of an image without reading the bytes and creating an image object. The main bottleneck here will be the http request overhead.

that said there are a few ways to speed up what you're trying to do.

  1. use threads to concurrently request images, then when all threads have finished processing output the images.

  2. If you display the same image or set of images more than once cache it. If you don't want to cache the actually image you can cache the metadata to avoid having to perform a http request for every image.

  3. decide if you need to output all the images to the page immediately, or could some or all of these be deferred and loaded via and ajax request

Chris Blackwell
  • 2,138
  • 17
  • 22
2

I have written this utility function quite a while ago (it runs on older ColdFusion versions, too). Maybe it helps.

Note that this requires the Java Advanced Imaging Image I/O Tools (Jai-imageio). Download the .jar and put it in your class path (restarting CF is necessary).

/**
 * Reads basic properties of many types of images. Values are
 * returned as a struct consisting of the following elements:
 *
 * Property names, their types and default values:
 *   ImgInfo.width       = 0        (pixels)
 *   ImgInfo.height      = 0        (pixels)
 *   ImgInfo.size        = 0        (bytes)
 *   ImgInfo.isGrayscale = false    (boolean)
 *   ImgInfo.isFile      = false    (boolean)
 *   ImgInfo.success     = false    (boolean)
 *   ImgInfo.error       = ""       (string)
 *
 * @param FilePath    Physical path to image file.
 * @return            A struct, as described.
 */
function GetImageProperties(FilePath) {
    var ImgInfo         = StructNew();
    var jImageIO        = CreateObject("java", "javax.imageio.ImageIO");
    var jFile           = CreateObject("java", "java.io.File").init(FilePath);
    var jBufferedImage  = 0;
    var jColorSpace     = 0;

    ImgInfo.width       = "";
    ImgInfo.height      = "";
    ImgInfo.fileSize    = 0;
    ImgInfo.isGrayscale = false;
    ImgInfo.isFile      = jFile.isFile();
    ImgInfo.success     = false;
    ImgInfo.error       = "";

    try {
        jBufferedImage      = jImageIO.read(jFile);
        ImgInfo.fileSize    = jFile.length();
        ImgInfo.width       = jBufferedImage.getWidth();
        ImgInfo.height      = jBufferedImage.getHeight();
        jColorSpace         = jBufferedImage.getColorModel().getColorSpace();
        ImgInfo.isGrayscale = (jColorSpace.getType() eq jColorSpace.TYPE_GRAY);
        ImgInfo.success     = true;
    }
    catch (any ex) {
        ImgInfo.error   = ToString(ex);
    }
    jImageIO = JavaCast("null", "");
    jFile = JavaCast("null", "");
    jBufferedImage = JavaCast("null", "");
    jColorSpace = JavaCast("null", "");

    return ImgInfo;
}

Use like:

imageInfo = GetImageProperties(theImageSrc);
if (imageInfo.success and imageInfo.width > 100)
    writeOutput('<img src="#HTMLEditFormat(theImageSrc)#" />');
}
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • With this function I get an error: "Can't read input file!" on line: "jBufferedImage = jImageIO.read(jFile);" (I only see this error once I remove the try / catch block of course) – Michael Giovanni Pumo Aug 13 '12 at 09:03
  • @MichaelGiovanniPumo You do use an absolute path of a local file, right? – Tomalak Aug 13 '12 at 09:13
  • No, it's an absolute path of a remote file - could be another server for example. Any way I can make this work that way? – Michael Giovanni Pumo Aug 13 '12 at 09:21
  • 1
    @MichaelGiovanniPumo I see. At the time of writing this answer this wasn't apparent to me. Well, as [Chris Blackwell said](http://stackoverflow.com/a/11931286/18771), the most waiting time will be spent during the HTTP request. You could try to use `` to make multiple HTTP requests simultaneously. Maybe that will cut down processing time? Chances are that the native `ImageNew()` and `ImageGetWidth()` are faster than my UDF above, so saving time with the HTTP requests might be your best option. – Tomalak Aug 13 '12 at 09:25