Skip to main content
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Shannon Hicks
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Shannon Hicks

If ColdFusion Had An ImageWriteToBinary() Function

By
Published in Comments (7)

Having native image functionality in ColdFusion is very handy. In Big Sexy Poems, I'm using it to generate Open Graph images on-the-fly for poems shared on social websites. But one thing that frustrates me about the image package is that Adobe ColdFusion always wants to rite images to a file. And I almost never want to write my images to a file. Instead, I want to render them directly to a binary object so that I can immediately send them to a remote storage bucket or return them in the HTTP response.

Aside: As an helpful parallel, this would be like having to write JSON (JavaScript Object Notation) to an intermediary text file anytime I wanted to serialize a data structure. I'm sure there are performance benefits that can be had from writing to a file; but, it's rarely what I want to do.

Since ColdFusion is an extremely flexible platform, we can create our own User Defined Functions (UDFs) for this purpose. But, this also means that we have to be good about cleanup-up after the file I/O; which is why it would be better if this were a native, battle-tested option.

That said, our polyfills could do nothing more than write the image the Adobe-mandated intermediary file, read-in the image binary, and then delete the intermediary file. To demonstrate, I've created two UDFs:

  • imageWriteToBinary( name [, format [, quality ]] )
  • imageWriteToBase64( name [, format [, quality ]] )

The first UDF returns a binary object. The second UDF proxies the first UDF and encodes said binary in as Base64-encoded string that can be used in a data URI:

<cfscript>

	image = imageNew( "", 700, 100, "rgb", "212121" );
	image.setAntialiasing( true );
	image.setDrawingColor( "ff3366" );
	image.drawText(
		"ColdFusion has FLEX'ibility",
		25,
		65,
		{ size: 40, font: "DejaVu Sans Mono Bold" }
	);

	// Embed image as base64-encoded data URI directly into HTML response.
	if ( true ) {

		writeOutput('
			<img src="data:image/png;base64,#imageWriteToBase64( image )#" />
		');

	// Write image as byte-array directly to response stream.
	} else {

		cfcontent(
			type = "image/png",
			variable = imageWriteToBinary( image )
		);

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	/**
	* I render the given image to a binary object.
	*/
	private binary function imageWriteToBinary(
		required any image,
		string format = "png",
		numeric quality = 0.75
		) {

		// Caution: we are blindly using the [format] in the file path. If the format
		// value were a user-supplied input, this could be dangerous! But, since this is
		// an internally-provided value, we can safely trust it for this demo.
		var destination = ( getTempDirectory() & "image-" & createUuid() & "." & format );

		try {

			imageWrite( image, destination, quality, false );

			return fileReadBinary( destination );

		} finally {

			fileDelete( destination );

		}

	}


	/**
	* I render the given image to a Base64 string.
	*/
	private string function imageWriteToBase64(
		required any image,
		string format = "png",
		numeric quality = 0.75
		) {

		return binaryEncode( imageWriteToBinary( image, format, quality ), "base64" );

	}

</cfscript>

In this demo, I have an if-condition to flip between the two UDFs. They both work; but the Base64 data URI gives us a more faceted exploration. When we run this CFML code, we get the following browser output:

As you can see, the ColdFusion image object was written directly to a Base64-encoded string, which I then embedded directly into the HTML response.

Being able to write the ColdFusion image object directly to a variable (whether it be binary or text) is just useful. And I think it makes the CFML code easier to read.

The ram:// Disk For Faster I/O

Having to render the ColdFusion image object to an intermediary disk is unnecessary ceremony. But, it's also slower than performing in-memory operations. Some of this performance gap can be mitigated by enabling the ram:// disk and then allowing ColdFusion to use the JVM's memory as a pseudo file system.

That said, in the age of Solid State Drives (SSD), I'm not sure how much of a difference that makes.

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

Reader Comments

365 Comments

I did some Googling, and came across a solution using the buffered image. This is just scratch code, but it worked:

<cfscript>

    image = imageNew( "", 700, 100, "rgb", "212121" );
	image.setAntialiasing( true );
	image.setDrawingColor( "ff3366" );
	image.drawText(
		"ColdFusion has FLEX'ibility",
		25,
		65,
		{ size: 40, font: "Serif" }
	);

    b64 = image.getBufferedImage();

    os = createObject("java", "java.io.ByteArrayOutputStream").init();
    imageIO = createObject("java", "javax.imageio.ImageIO");
    imageIO.write( b64, "png", os );
    b64 = toBase64( os.toByteArray() ); 

</cfscript>

<cfoutput>
    <img src="data:image/png;base64,#b64#" alt="ColdFusion Image">
</cfoutput>
16,124 Comments

@Raymond,

That's a good point - if you want to dip down into the Java layer to use the underlying buffered image stuff you can side-step the file. But, I think that's the raw data (ie, not the data in a particular format such as PNG or JPG). With the buffered image, I think you would still then have to do the actual compression stuff.

I'm not against going down that path; I just think it would be cool if ColdFusion exposed it more readily.

16,124 Comments

Ahh, nice, just saw your second comment. Thanks for the follow-up proof-of-concept. I'd still like to wrap it up in a nice abstraction; but, good to know that the abstraction isn't just a wrapper around imageWrite().

16,124 Comments

This is all a reminder to me that I know very little about the java.awt.image and javax.imageio packages. It's probably worth me poking around in them a bit. I'm sure there's much gold to be had in them there hills.

Post A Comment — I'd Love To Hear From You!

Post a Comment

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
Managed hosting services provided by:
xByte Cloud Logo