Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Pradnya Kambli
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Pradnya Kambli

ImageGetEXIFMetadata() Requires Existing File For First Read In ColdFusion

By
Published in

Yesterday, at InVision App, I was started to experiment with EXIF (Exchangeable Image File) image data for the first time in ColdFusion. I was doing so in an area of the code that consumed a temporary file that was deleted right before returning a ColdFusion Image object. When I then went to call imageGetEXIFMetadata() on the returned image object, I was getting an error. As it turns out, the first call to imageGetEXIFMetadata() needs the underlying physical file to exist.

To see this in action, we can copy a file to a temp location, read it in as a ColdFusion image object, delete the temp file, and then try to call imageGetEXIFMetadata():

<cfscript>

	permPath = expandPath( "./image.jpg" );
	tempPath = expandPath( "./temp.jpg" );

	// Here, we are going to copy the binary file to a temp location for purposes that
	// are beyond the scope of this exploration. But, notice that after we copy the PERM
	// file, we read it in as a ColdFusion image object and then delete the TEMP file.

	fileCopy( permPath, tempPath );

	image = imageRead( tempPath );

	fileDelete( tempPath );

	// After we delete the underlying TEMP file, we are still able to access most
	// properties of the image object, such as its pixel data and its dimensions.
	width = imageGetWidth( image );
	height = imageGetHeight( image );
	writeOutput( "Dimensions: #width# x #height#." );

	// BUT, an attempt to read the EXIF data from an image object with no physical
	// file backing, results in a ColdFusion error.
	writeDump( imageGetEXIFMetadata( image ) );

</cfscript>

When we run this code, we're able to read the image dimensions. But, when we get to the imageGetEXIFMetadata() function call, we get the following ColdFusion error:

Unable to read image source properly. /Applications/ColdFusion11/cfusion/wwwroot/cf11/exif/temp.jpg (No such file or directory)

As it turns out, ColdFusion doesn't read in the EXIF data when it creates the initial image object. As such, the first time that we call imageGetEXIFMetadata() on the image object, ColdFusion has to go back to the underlying physical file in order to access the metadata. At that point, according to the documentation, the EXIF data gets cached in memory:

The result of the ImageGetEXIFMetadata function is cached in the ColdFusion image to optimize performance. The ImageGetEXIFMetadata function applies only to JPEG images. If you try to retrieve metadata for Base64, BLOB, or other types of images, ColdFusion generates errors.

So, going back to my workflow with temporary image files, we can get around this by forcing ColdFusion to read the EXIF data into memory before we delete the temp file. This way, even if we don't consume the EXIF data directly within the workflow, we can ensure that the calling context (that receives the Image object) can read the EXIF data if it needs to.

<cfscript>

	permPath = expandPath( "./image.jpg" );
	tempPath = expandPath( "./temp.jpg" );

	// Here, we are going to copy the binary file to a temp location for purposes that
	// are beyond the scope of this exploration. But, notice that after we copy the PERM
	// file, we read it in as a ColdFusion image object and then delete the TEMP file.

	fileCopy( permPath, tempPath );

	image = imageRead( tempPath );

	// Before we delete the TEMP file, let's read in the EXIF data. At this point,
	// we don't have to consume it; but, asking for it causes it to be read from the
	// physical file and then cached in memory. This way, subsequent requests for the
	// EXIF data (on this image object) will be pulled out of memory.
	imageGetEXIFMetadata( image );

	fileDelete( tempPath );

	// After we delete the underlying TEMP file, we are still able to access most
	// properties of the image object, such as its pixel data and its dimensions.
	width = imageGetWidth( image );
	height = imageGetHeight( image );
	writeOutput( "Dimensions: #width# x #height#." );

	// This time, the request for the EXIF data is pulled out of the in-memory Image
	// object rather then being pulled from the underlying (already deleted) file).
	writeDump( imageGetEXIFMetadata( image ) );

</cfscript>

As you can see, we're calling imageGetEXIFMetadata() and discarding the result right before deleting the underlying temp file. This forces ColdFusion to read in and cache the EXIF data such that the subsequent call to imageGetEXIFMetadata() can pull the data right out of memory. And, when we run the above code, we get the following page output:

Accessing imageGetEXIFMetadata() in ColdFusion after the physical image was deleted.

I assume that ColdFusion uses this approach because reading EXIF data is a relatively expensive process and likely unneeded for the majority of image operations. But, I have to admit, ColdFusion image manipulation has a ton of limitations around how files can be read in and consumed, including the fact that image files with non-image file extensions (such as ".tmp") are all but useless in certain file operations. It makes me sad.

As a side note, I actually ended up ditching ColdFusion's imageGetEXIFMetadata() function for this work altogether and started using ImageMagick's "-auto-orient" command to adjust the orientation of the underlying physical file before I read it into memory as a ColdFusion image object.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel