Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2011 (Minneapolis, MN) with: Barney Boisvert

ColdFusion 8 Image Manipulation Web Service

By Ben Nadel on
Tags: ColdFusion

The other day, I was talking to my friend and technology cohort, David Stamm, who was having a development environment issue. Basically, he was doing some work on a ColdFusion 7 box and needed to do some image manipulation. Having been on a ColdFusion 8 box for a while, I felt out of touch with the CF7 world and I didn't even know what to recommend. He ended up going with Rick Root's Image.cfc up on RIAForge.org; but, the whole thing got me thinking about distributed image manipulation. I did a quick Google search to see if there were any existing image manipulation web services. I didn't find anything. I took a look on www.xmethods.net to see if there were any there - a few CAPTCHA methods and a conversion method, but nothing substantial.

It seems that there are no image manipulation web services, so, I thought I would take a crack at building one that wraps around ColdFusion 8's built-in image manipulation functionality. I am very new to creating both web services and APIs, so I wanted to keep it simple. I didn't even bother looking up any web service "best practices" or SOAP standards because I wanted to see how simple I could possibly make this API. And to me, when it comes to structured data, there's nothing more straightforward than a light weight XML packet.

When you send the image manipulation commands via XML, you can't send the image binary; XML only supports text-based data. As a result, you need to encode the image binary in Base64 format. I assume that this ability is part of most any programming language. Here is what a sample XML post would look like:

  • <request>
  • <image>
  • <data>.....base64..data.....</data>
  • </image>
  • <commands>
  • <command ..... />
  • <command ..... />
  • <command ..... />
  • <command ..... />
  • </commands>
  • <export format="jpg" quality=".8" />
  • </request>

There is very little extraneous data here. I am passing in the image data in Base64 format. Then, I pass a list of command nodes which will define how the image is manipulated. Then, finally, I pass an export node that defines how the image will be returned (the image is returned in Base64 encoded as well).

When the data comes back from the web service, it either comes back as a successful process or a failure. These are have two different but extremely simple XML structures. The failure looks like this:

  • <response success="no">
  • <error>
  • <message>.....</message>
  • <detail>.....</detail>
  • </error>
  • </response>

... and the successful return looks like this:

  • <response success="yes">
  • <image>
  • <data>.....base64..data.....</data>
  • </image>
  • </response>

The successful response returns the new image in Base64 encoding.

Now, like I said, I know this doesn't stick to any web service standards, but this seems really simple and straightforward to me. I am not sure what benefit adding something like a SOAP wrapper or an XML-RPC style formatting would add (but if you have a good argument, please let me know and I can update it).

I am not supporting a lot of different commands at this time (I wrote this last night and this morning). This is more a proof of concept and to see if anyone would be interested in having me flesh this out any more than it is. Right now, these are the command nodes that I support:

  • <command
  • name="blur"
  • radius="[3-10]"
  • />
  •  
  • <command
  • name="border"
  • color=""
  • thickness="px"
  • />
  •  
  • <command
  • name="flip"
  • direction="vertical|horizontal|diagonal|antidiagonal"
  • />
  •  
  • <command
  • name="grayscale"
  • />
  •  
  • <command
  • name="negative"
  • />
  •  
  • <command
  • name="resize"
  • width="px|%"
  • heigh="px|%"
  • />
  •  
  • <command
  • name="rotate"
  • angle=""
  • />
  •  
  • <command
  • name="scaletofit"
  • width="px"
  • height="px"
  • />
  •  
  • <command
  • name="sharpen"
  • gain="[-1-2]"
  • />

It's a small subset of ColdFusion 8's image manipulation functionality, but enough to do some cool stuff (such as creating thumbnails for user-uploaded images). Now that you see how the API works and what commands are available, let's play around with it a little bit. Let's take the following image and use the image manipulation service to modify it:


 
 
 

 
Two Naughty Girls Touching Each Other In Their Panties  
 
 
 

If you want to play around with this at all, the image manipulation web service URL is:
http://www.bennadel.com/resources/webservices/image/

The following demonstration uses ColdFusion 7 functionality to post XML to my image manipulation web service:

  • <!--- Read in the binary image data. --->
  • <cffile
  • action="readbinary"
  • file="#ExpandPath( './two_naughty_girls_touching.jpg' )#"
  • variable="binImage"
  • />
  •  
  •  
  • <!--- Create the XML request data. --->
  • <cfxml variable="xmlRequest">
  • <cfoutput>
  •  
  • <request>
  • <!--- The image data in Base64 format. --->
  • <image>
  • <data>#ToBase64( binImage )#</data>
  • </image>
  •  
  • <!--- The commands to perform on the image. --->
  • <commands>
  • <command name="resize" width="90%" height="90%" />
  • <command name="flip" direction="horizontal" />
  • <command name="negative" />
  • <command name="border" color="##739EBD" thickness="10" />
  • </commands>
  •  
  • <!--- The format of the returned image. --->
  • <export format="jpg" quality=".8" />
  • </request>
  •  
  • </cfoutput>
  • </cfxml>
  •  
  •  
  • <!--- Post the XML request. --->
  • <cfhttp
  • url="http://www.bennadel.com/resources/webservices/image/"
  • method="post"
  • result="objGet">
  •  
  • <!---
  • Post XML as xml param. This will set the body and
  • the appropriate mime-type.
  • --->
  • <cfhttpparam
  • type="xml"
  • value="#xmlRequest#"
  • />
  •  
  • </cfhttp>
  •  
  •  
  • <!--- Parse the response file content XML. --->
  • <cfset xmlResponse = XmlParse( objGet.FileContent ) />
  •  
  • <!--- Check to see if it was successful. --->
  • <cfif xmlResponse.response.XmlAttributes.success>
  •  
  • <!---
  • Convert the Base64 image data back to binary and then
  • write it to the file system.
  • --->
  • <cffile
  • action="write"
  • file="#ExpandPath( './result.jpg' )#"
  • output="#ToBinary( xmlResponse.response.image.data.XmlText )#"
  • />
  •  
  • <!--- Display new image. --->
  • <img src="./result.jpg" />
  •  
  • <cfelse>
  •  
  • <p>
  • An error occurred.
  • </p>
  •  
  • <p>
  • #xmlResponse.response.error.message.XmlText#
  • </p>
  •  
  • <p>
  • #xmlResponse.response.error.detail.XmlText#
  • </p>
  •  
  • </cfif>

As you can see, we are reading in the image binary and then creating the simple XML packet using ColdFusion 7's CFXml tag. This XML data is then posted to the image manipulation web service using the CFHttp tag with a single CFHttpParam tag. Take special note that we are POSTing the data, not GETting it. POSTing is required for the web service to get access to the document body.

Once the web service returns, we check to see if there was an error (as denoted by the success attribute). If it is successful, we take the returned Base64 data and write it as binary to the file system. Then, just to make sure that everything worked well, I am displaying the image using a standard HTML IMG tag.

Running the above code, we get the following output (new image):


 
 
 

 
Two Naughty Girls Touching Each Other In Panties After Photo Manipulation  
 
 
 

As you can see, the remote call to my image manipulation web service took the given commands and returned the new image. I thought this was kind of a cool idea and very easy to use (from the XML-API view point). Any one have any thoughts on it? Think it is worth pursuing? One of the things that I really like about this idea is that it is language independent - anyone who can create an XML packet and post it can leverage the power of ColdFusion 8's image manipulation functionality. As you know, I love ColdFusion 8 and I want to get it out there, helping as many people as possible.



Reader Comments

I definitely think it's worth perusing, but I don't think it should be a free service. Those image libraries are pretty taxing and the speed of them isn't as great as it could be. It's fast when there's 1-2 requests, but this would definitely create a queue of requests quickly if you don't put a floodgate in somewhere.

Reply to this Comment

There must be something going around--and maybe it's called CF 8 (grin)--but a lot of folks have been messing around with the new image functions lately. You did your ImageUtils.cfc and now this web service, and both Joshua Cyr and I have developed JavaScript-powered tools for letting end-users arrange text and color on top of existing images and then convey that data to the image functions.

I like this service idea of yours. But given that each webservice call contains the data for an entire image, do you think it would scale up if the service got heavy use or if folks transmitted large, print-quality images for manipulation?

Reply to this Comment

Very nice!

It would be nice to take this approach to take advantage of the image manipulation on CF8 from servers that haven't upgraded yet.

Reply to this Comment

Maybe I should have my image site start using your service. :-)

Regarding the processing power, it would be simple enough to have to submit an app ID with the request which you authenticate, so at least you know who is doing what. You could even limit it to x number of requests a day like amazon does.

Services like this are handy for CMS's and distributed apps where you don't always know what the server has available. So pinging another server for actions like this, or spell checking, then returning result is quite handy. I haven't played with AIR (yet), but I can imagine it could be useful in that regard too.

Speaking of which... spellchecking. Now that is something begging a solution in CF I think. Other than tapping into aspell or the like.

Reply to this Comment

@All,

Yeah, this was more a proof of concept than anything else. I wanted to see if would get any sort of traction or if it would just get ignored. I think it is definitely something that, if implemented correctly would require some sort of DeveloperID or something such that you would need to sign up for the service and your usage could be tracked.

Now that I see how a Base64 image can be transmitted over a web service and returned, the rest is just figuring out how to make it better.

@Brian,

I would think (*hope*) that is someone is dealing with large, print-quality images, they are not using web-based photo manipulation... but that is a good point. I could easily put a file size limit on the data.

Reply to this Comment

@Ben: Putting a file size doesn't help. You had to "accept" that asinine file size first before you could get it all and reject it. I wish CF had a way to throttle the incoming request and if it exceeded the max buffer size, throw an exception.

Reply to this Comment

@Todd,

True, ColdFusion would still have to accept the file, but at least I could prevent the processing from taking place.

Reply to this Comment

i just thought of this, but maybe what you can do Ben is use Amazon's EC2 and S3 services to put something together.

You could probably start a slew of these. I know a lot of people would also love a cfdocument type webservice.

Reply to this Comment

@Todd,

I never even though of going beyond images... very interesting. Unfortunately, I think document creation is far to processing intensive to be viable in this way.

Reply to this Comment

@todd,

that's why you charge something on a per usage basis or a monthly fee. If anyone know if Railo has a cfimage, then your set Ben.

Railo community is free to use for any use and they support the cfdocument tag.

Reply to this Comment

@Tony: From everything I'm reading online from people that switched to the Amazon cloud, it went something like this.

Dev 1: We need to scale quickly!
Dev 2: Amazon to the rescue!
Dev 1: Well, that was easy enough.
-- time passes --
Dev 1: Holy F'ing crap?! $10,000 bill from Amazon?!
Dev 2: Maybe we scaled too much?

heh. Yeah, ad income aren't enough to pay for S3 service. It'd have to be a pay-per-usage thing.

Reply to this Comment

Ben,

Nice, though you could have warned about the image, lol. Looking at the site at work, well ummm.........

Reply to this Comment

I need this ! Is there any free (or commerical) service that's like it available ?

Reply to this Comment

Regarding the processing power, it would be simple enough to have to submit an app ID with the request which you authenticate, so at least you know who is doing what. You could even limit it to x number of requests a day like amazon does.

Services like this are handy for CMS's and distributed apps where you

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.