PhoneGap file upload to ColdFusion

This post is more than 2 years old.

This came up on the PhoneGap Forums today so I thought I'd take a quick look at how PhoneGap handles file uploads. Turns out there is really nice support for it built-in, but you can quickly run into an issue with ColdFusion if you don't know one little tip.

My demo application will make use of PhoneGap's FileTransfer object. What's nice is that the PhoneGap team includes a full demo that makes use of your device's photo library. I decided I'd use this demo to post a file to ColdFusion and perform a few quick image manipulations to it. Let's begin with the PhoneGap portion of the code. My HTML is rather simple. I've got a button and some elements that will end up storing results later on.

<!DOCTYPE HTML> <html> <head> <meta name="viewport" content="width=320; user-scalable=no" /> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title>Image Upload Example</title> <link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title" charset="utf-8"> <script type="text/javascript" charset="utf-8" src="phonegap-1.1.0.js"></script> <script type="text/javascript" charset="utf-8" src="xui-2.3.0.js"></script> <script type="text/javascript" charset="utf-8" src="main.js"></script> </head>

<body onload="init();">

&lt;button id="picSelect"&gt;Select Picture&lt;/button&gt;

&lt;div id="status"&gt;&lt;/div&gt;

&lt;img id="resultpic"&gt;

</body> </html>

As you can guess, the main logic is in main.js. Let's take a look over there.

function init() { document.addEventListener("deviceready", deviceReady, true); }

function errorHandler(e) { /* FileTransferError.FILE_NOT_FOUND_ERR = 1; FileTransferError.INVALID_URL_ERR = 2; FileTransferError.CONNECTION_ERR = 3; */ alert("Error: "+JSON.stringify(e)); }

function picDone(loc) { x$("#status").html("after","About to upload your picture...");

var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=loc.substr(loc.lastIndexOf('/')+1);
options.mimeType="image/jpeg";
//Thank you Steve Rittler! http://www.countermarch.com/blog/index.cfm/2011/10/27/PhoneGap-FileTransfer-and-ColdFusion
options.chunkedMode=false;

var ft = new FileTransfer();
ft.upload(loc, "http://192.168.1.105/test3a.cfm", fileUploaded, errorHandler, options);

}

function fileUploaded(r) { x$("#status").html("And we're done!"); x$("#resultpic").attr("src", r.response); }

function deviceReady() {

x$("#picSelect").touchstart(function(e) {
    navigator.camera.getPicture(picDone,errorHandler,{sourceType:Camera.PictureSourceType.PHOTOLIBRARY, destinationType:Camera.DestinationType.FILE_URI,quality:50}); 
});

}

Let's walk through this, starting with the deviceReady function. That's run because I added a listener to it in my init function and is a way to ensure I can do "cool device stuff" with the PhoneGap APIs. In case you're curious about the x$ stuff - that's just me playing with xui.js, a replacement for jQuery. I'm not sure how I feel about it yet - ask me next week.

Any way, you can see where we bind to the button element's touch event. When run, we ask the device to get a picture. PhoneGap allows you to go the camera or to the storage for the picture. In this case I went to my storage. Once the picture is taken, we then begin the file upload process. This is in the function picDone. The code here is pretty much ripped right from the PhoneGap docs, with one crucial difference. Notice the call out to a blog post by Steve Rittler. Apparently the upload is using chunked form data. ColdFusion can't handle this. For the life of me though I thought it was an Apache issue. I got a 411 error in Apache, but nothing in ColdFusion. I'm still not convinced it is a ColdFusion, but at the end of the day, Steve's change worked fine. By the way, "fileKey" is simply the name of the form field. You will need to remember this when we get over to the server side.

Finally, our file upload handler fileUploaded() assumes we are getting a URL back. It then simply takes that URL and assigns it to the image. Here's a few screen shots. First, the application as it begins:

Next - the image picker....

and finally, the result:

The server side code is rather trivial:

<cfsetting enablecfoutputonly="true">

<cfif structKeyExists(form, "file")> <cfset destination = getTempDirectory()>

&lt;cffile action="upload" filefield="file" destination="#destination#" nameconflict="makeunique" result="result"&gt;

&lt;cfif result.fileWasSaved&gt;
	&lt;cfset theFile = result.serverDirectory & "/" & result.serverFile&gt;
	&lt;cfif isImageFile(theFile)&gt;
		&lt;!--- copy to web root with new name ---&gt;
		&lt;cfset newName = expandPath("./") & createUUID() & ".jpg"&gt;
		&lt;cfset fileMove(theFile, newName)&gt;
		&lt;!--- resize to a thumbnail and grayscale for the hell of it ---&gt;
		&lt;cfset img = imageRead(newName)&gt;
		&lt;cfset imageScaleToFit(img, 200,200)&gt;
		&lt;cfset imageGrayScale(img)&gt;
		&lt;cfset imageWrite(img)&gt;
		&lt;cfoutput&gt;http://192.168.1.105/#getFileFromPath(newName)#&lt;/cfoutput&gt;
	&lt;cfelse&gt;
		&lt;cfset fileDelete(theFile)&gt;
	&lt;/cfif&gt;

	
&lt;/cfif&gt;

</cfif>

You can see I handle the file upload, do some basic checking, and if it is an image, I scale the size and gray scale the color. I then simply output the URL. I could have written this as a CFC of course and normally would. Outside of the darn chunked error, this is a rather simple process. I'm not sure why this chunked option isn't documented (I posted as such to the forums), but now that I'm past it, I'm pretty pleased with how easy PhoneGap makes this.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Mark Drew posted on 11/3/2011 at 3:58 PM

I looked into this a while back, and the other way its to save the image binary that you get back from the camera into a text field and submit that to the server, some toBase64 functions later, and you are done. although I do like the fileUpload functionality!

Comment 2 by Raymond Camden posted on 11/3/2011 at 4:56 PM

The issue with the Base64 string though is that it can be huge. I've seen PG apps crawl to a halt when using Base64. You can ask for lower quality/size pictures of course.

Comment 3 by Jonas Hexadecimal posted on 1/3/2012 at 8:52 PM

Ray,

Get rid of the beard. Really.

Comment 4 by Raymond Camden posted on 1/3/2012 at 8:53 PM

Eh? You mean in the one above? That's an old picture. The one you see here in comments is more recent. And my wife likes it. She trumps all comments. ;)

Comment 5 by Martin posted on 2/24/2012 at 3:12 PM

Hi Ray,

Have you experience multiple file upload? want to upload video,audio,image in one request..http multipart...or any other alternatives?

thx

Comment 6 by Raymond Camden posted on 2/24/2012 at 4:27 PM

As far as I know, you can't do it using this API.

Comment 7 by Martin posted on 2/24/2012 at 4:33 PM

hi Ray,

ok..thought that..mhhh...what are the alternatives..REST ?

Comment 8 by Raymond Camden posted on 2/24/2012 at 4:38 PM

I'd do it in N async calls. Using jQuery Deferreds, it should be a bit easier to manage.

Comment 9 by Martin posted on 2/24/2012 at 10:11 PM

Hi Ray,

thx for the hint with deferred, - this means using ft.upload in combination with deferred?! I was also thinking about kind of pack all files together and POST it in one request ;) ?! any creative ideas ;)

Comment 10 by Raymond Camden posted on 2/24/2012 at 10:14 PM

In theory, stress, in theory, you could make a zip using JavaScript and then post that.

http://gildas-lormeau.githu...

Comment 11 by Martin posted on 2/24/2012 at 10:29 PM

ok thx.

Comment 12 by Steven Benjamin posted on 6/12/2012 at 4:05 AM

Thanks Ray, This post saved me enough time so I could run out and pick up a bottle of Blanche De Chambly!

Comment 13 by Raymond Camden posted on 6/12/2012 at 4:11 AM

I keep thinking as I mature, I will eventually like wine. Still hasn't happened. ;)

Comment 14 by Joe Velez posted on 10/21/2012 at 12:26 AM

Ray - I have used your site for a plethora of coding solutions. Thanks! ...Anyway, I am using PhoneGap 2.1 and have my emulator posting to a .cfm file but I get an http_status 500 error -- yet, when I change it to a .php script (heaven forbid I use php) it works fine. My CFM and PHP files are both blank! So why would CFM not work, and PHP work? Actually, why does CFM return a 500 and how do I fix it? I'm running Apache 1.3 CF8 PHP5 -- Thanks in advance!

Comment 15 by Raymond Camden posted on 10/21/2012 at 12:30 AM

Did you include the link that set chunkedmode?

Comment 16 by Joe Velez posted on 10/21/2012 at 12:38 AM

Yes.

I'm using the 'full example' code provided by PhoneGap (the latest verion). I have been google-ing around the past few days and have tested a variety of settings including:

options.chunkedMode = false;

I even tried

ft.upload(imageURI, "[MY_CFM_URL]", win, fail, options, true);

I have edited my Config.xml and set

<access origin="[MYURL]"/>
<access origin="*"/>

I literally changed my 'uploadtest.cfm' to 'uploadtest.php' and by doing that I don't receive the http_status 500 FileNotFoundException error.

Yes, both files exist on my server, and I can access them in the emulator's browser just fine.

Comment 17 by Raymond Camden posted on 10/21/2012 at 12:45 AM

You said 500 in your first comment, then 500 File Not Found in your second. A File Not Found error is 404. Are you sure it isn't that? What do the CF logs say? The logs probably have a more verbose error.

Comment 18 by Joe Velez posted on 10/21/2012 at 1:12 AM

err i meant the emulator logs showed http_status 500 along w/ the FileNotFoundException error ... anyway, I'm a dummy! Usually I can see the errors in the browser and didn't even think to look at the logs. When I did I saw an error on some line, and realized I had some underlying Application code going on for my site. Moved my script into it's own folder with an empty Application.cfm and voila, it works! Thanks for the slap in the face to remind me to actually look at the logs - that's why they're there. Thanks for taking the time to help me out. I'll get you back

Comment 19 by Raymond Camden posted on 10/21/2012 at 5:44 AM

Just glad ya got it.

Comment 20 by EC List posted on 12/8/2012 at 12:46 AM

Raymond,

Thank you for all of your phonegap posts. You've been a huge help to me. I have a (possibly stupid) question about the file upload example. Since you aren't using a CFFUNCTION to process the file when it is uploaded, how would I send back a confirmation to my server that the file upload when alright? I am kind of new to this kind of development, so I am sorry if the answer is obvious.

Thank you again.

Comment 21 by Raymond Camden posted on 12/8/2012 at 12:56 AM

I did send a response. Consider this part:

<cfoutput>http://192.168.1.105/#getFileFromPath(newName)#</cfoutput>

That response is returned to the PhoneGap app and used here:

x$("#resultpic").attr("src", r.response);

Comment 22 by EC List posted on 12/8/2012 at 12:59 AM

Doh! Ok. Got it. Thanks again.

Comment 23 by Johann posted on 2/10/2013 at 3:06 PM

Ray, you are such an inspiration..your work is priceless and im so glad i can talk to you through this blog

Comment 24 by Raymond Camden posted on 2/10/2013 at 8:17 PM

Wow, thank you. :)

Comment 25 by Terry Collinson posted on 3/9/2013 at 5:44 PM

Hi Ray, Do you know of any way I can resize the image before I upload. It only need to be 300 pixels wide and any height. I found this link http://stackoverflow.com/qu... and assume the naswer is buried in there somewhere.

Comment 26 by Raymond Camden posted on 3/9/2013 at 7:23 PM

If this is a *new* picture, you can specify a targetWidth/Height before uploading. If this is an existing picture, then you would need to use what is described in the SO link. Is it not making sense to you?

Comment 27 by Terry Collinson posted on 3/10/2013 at 6:11 PM

Its an existing picture and no it does not quite make sense. There appears to be two methods discussed in the link - manipulating the file in base 64 format or using canvas. Neither of which I am that familiar with. Any chance of 'whipping up' a quick example? Thanks Terry

Comment 28 by Raymond Camden posted on 3/10/2013 at 6:24 PM

Sure, but it may take a little while. I'm on the road this week.

Comment 29 by Terry Collinson posted on 3/11/2013 at 11:14 AM

I can wait - thanks a lot

Comment 30 by Raymond Camden posted on 6/4/2013 at 5:54 PM

Terry, sorry this has taken me so long. I still want to work an example that shows resizing an image, but I wanted to be sure you knew that if you are using a *new* image, do not forget that the PhoneGap Camera API lets you specify a target size.

Comment 31 by Terry Collinson posted on 6/5/2013 at 12:24 PM

Hi Ray, Thanks yes I am using that. I have been routing around myself and this link seems to be the closest to a solution - http://pastebin.com/edACk56q

Comment 32 by Terry posted on 6/13/2013 at 7:12 PM

As my first attempt at building a phonegap app. Where do I find the *.js files referenced in the code and what is the xui?

Comment 33 by Raymond Camden posted on 6/13/2013 at 8:48 PM

1) JS FIles.

phonegap.js comes from the PhoneGap SDK you download.
main.js is the second code sample I believe.
xui.js comes from http://xuijs.com/. xui is a light-weight jQuery clone.

Comment 34 by Soniya posted on 1/24/2014 at 11:39 AM

Hi Sir,
I have another issue. i can easily upload photo and video on server. but now i want to upload audio file on server. i want just like photo upload user will select audio file from list and upload that on server.whatever i got from phonegap api and google , is first record audio and upload that, but i don't want to record, just select audio file from list and upload it.
It would be great help if you give any idea.

Comment 35 by Raymond Camden posted on 1/24/2014 at 8:28 PM

Well, where does the list of audio files come from? If the file system, then you should look at the docs for the File System stuff.

Comment 36 by Soniya posted on 1/27/2014 at 2:37 PM

Yes the audio will come from file system.
But i want the same procedure just like photo and video , user will click on upload button then file system(play list ) will open ,user will select audio file from there and upload.
I am very confuse as photo and video uploading is very easy, can't this also be easy for audio.I don't have very much idea about file system apis.

Comment 37 by Raymond Camden posted on 1/27/2014 at 10:47 PM

You can use the Capture API (http://docs.phonegap.com/en... to capture new audio, but you want existing audio - and that has to come from a file. You can't get the same UI you get with camera as that just isn't an option.

I'd recommend spending some time reading the docs on the file system stuff. It isn't friendly per se, but you need to learn it.

Comment 38 by Soniya posted on 1/28/2014 at 11:34 AM

Thanks for you reply.. i know about the capture api and it works great. but my need is different like upload existing audio.i'll surely read the file system stuff.
If you get some time please have a look and give me clue regarding this .

Comment 39 by Raymond Camden posted on 1/28/2014 at 9:07 PM

I did give you a clue. :) Read up on the file system stuff. You need to at least be prepared to read a directory and display the results to screen, which if you check the docs, should be doable.

Comment 40 by Paul Baylis posted on 1/17/2016 at 4:47 AM

I'm probably doing something heinous, but I can't get the "select picture" button to fire anything, not even an alert. I know all my js is loading properly and no js errors coming through Firebug when I test locally. Any idea?

Comment 41 (In reply to #40) by Raymond Camden posted on 1/18/2016 at 2:38 PM

Use remote debugging to see if an error is being thrown.

Comment 42 by nvie posted on 1/9/2018 at 7:56 AM

Need to upload file with chunked data. Do i need to change in server side code?

Comment 43 (In reply to #42) by Raymond Camden posted on 1/9/2018 at 3:59 PM

You would, yes.