Shan’s Simple Examples: File uploads with Flex and ColdFusion

Someone in #flex was talking about how there were no good examples for doing uploads with Flex and ColdFusion. Sounded like an excellent topic to cover here. Now this example is a tiny bit more complex than previous examples, because I needed to cover two methods of functionality: single file uploads and multi-file uploads. Both processes are very similar, and in my code they actually share a method.

Here’s the MXML application:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()" layout="absolute">
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            private var myFileList:FileReferenceList;
            private var myFile:FileReference;
            private var uploadTarget:URLRequest = new URLRequest("upload.cfm");
           
            private function init():void {
                myFileList = new FileReferenceList();
                myFileList.addEventListener(Event.SELECT,fileListSelected);
               
                myFile = new FileReference();
                myFile.addEventListener(Event.SELECT,fileSelected);
            }
           
            private function fileListBrowse():void {
                // we only want to allow images to be uploaded
                var imagesFilter:FileFilter = new FileFilter("Images", "*.jpg;*.jpeg");
                myFileList.browse([imagesFilter]);
            }
           
            private function fileBrowse():void {
                // we only want to allow images to be uploaded
                var imagesFilter:FileFilter = new FileFilter("Images", "*.jpg;*.jpeg");
                myFile.browse([imagesFilter]);
            }
           
            private function fileListSelected(e:Event):void {
                // here we could do whatever we want, but we're just going to
                // upload right away
                uploadFromList();
            }
           
            private function fileSelected(e:Event):void {
                // here we could do whatever we want, but we're just going to
                // upload right away
                uploadFile(myFile);
            }
           
            private function uploadFromList():void {
                if (myFileList.fileList.length > 0) {
                    // if there are still files left to upload, continue
                    myFile = myFileList.fileList[0] as FileReference;
                    uploadFile(myFile);
                } else {
                    // if there are no more files, stop
                    return;
                }
            }
           
            // NOTE: this method is used for one or multiple files
            private function uploadFile(f:FileReference):void {
                // here we build out the other form items
                var urlVars:URLVariables = new URLVariables();
                urlVars.myFirstValue = 1;
                urlVars.myOtherValue = "something else";
               
                // add the form items to the request
                uploadTarget.data = urlVars;
                // set request the method to POST
                uploadTarget.method = URLRequestMethod.POST;
               
                // add the event listeners
                // DataEvent.UPLOAD_COMPLETE_DATA only fires if data is returned and the flash player supports it
                // otherwise, use Event.COMPLETE
                f.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA,onDataComplete);
                f.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
                f.addEventListener(ProgressEvent.PROGRESS, progressHandler);
               
                // show the progress bar
                myProgress.visible = true;
               
                // start the upload
                f.upload(uploadTarget,"myUploadFieldName");
            }
           
            private function ioErrorHandler(e:IOError):void {
                // there was some error, you'll want to tell someone
                Alert.show("Error occured, oops!","I/O Error");
            }
           
            private function progressHandler(e:ProgressEvent):void {
                // update the ProgressBar
                myProgress.setProgress(e.bytesLoaded,e.bytesTotal);
            }
           
            private function onDataComplete(e:DataEvent):void {
                // here we could handle whatever was returned by the server. XML is probably your best bet.
                // var myResult:XML = XML(e.data); // would work for XML
                var myResult:String = e.data;
               
                // if there were multiple files, delete the one we uploaded and try another
                if (myFileList.fileList.length > 1) {
                    // remove what we just uploaded
                    myFileList.fileList.splice(0,1);
                    // go upload another another
                    uploadFromList();
                } else {
                    // if this was the last of a multiple file upload, remove it
                    if (myFileList.fileList.length > 0) {
                        myFileList.fileList.splice(0,1);
                    }
                   
                    // we're done uploading
                    myProgress.visible = false;
                    return;
                }
            }
        ]]>
    </mx:Script>
    <mx:VBox height="100%" width="100%" horizontalAlign="center" verticalAlign="middle">
        <mx:Button label="Choose One File to Upload..." click="fileBrowse()"/>
        <mx:Button label="Choose Multiple Files to Upload..." click="fileListBrowse()"/>
        <mx:ProgressBar id="myProgress" mode="manual" label="Uploading" visible="false"/>
    </mx:VBox>
</mx:Application>

The comments point out all the nuances you need to know about. It does the major features, including passing more data with the file, what you can do with the progress event, how to do an upload queue, rather than uploading all selected files at the same time, and how to handle responses from the upload script. This code will work with any backend processor, not just ColdFusion.

Here’s the ColdFusion file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<cfsetting enablecfoutputonly="true">
<!--- this will upload the file --->
<cffile action="upload" fileField = "myUploadFieldName" destination="#expandPath("./")#uploads/">

<!--- note that all formfields including FORM.myFirstValue and FORM.myOtherValue --->
<cfscript>
    myVal1 = FORM.myFirstValue;
    myVal2 = FORM.myOtherValue;
</cfscript>

<!--- here you can send stuff back to flex, only if the flash player is 9.0.28.0 or later --->
<cfoutput><myXML>
    <file uploadedAs="#cffile.serverFile#">
</myXML></cfoutput>

It doesn’t do much, but you can see how to get non-file data passed from flex, and how to send a response. Make sure that, when sending a response, you don’t send any whitespace before what you intend to send back to Flex. Also, note that your users need the Flash Player version 9.0.28.0 or later in order to send responses back to flex.

Comments

Apple’d and loving it

I’ve just realized that over the past 12 months I’ve gone Apple. It started innocently enough with a Juniper iTunes credit card and an iPhone on launch day last June. Then an Apple TV found it’s way into my home. Eventually, a MacBook and a Mac Pro followed suit.

I have to say I’m impressed with all my Apple products. Here’s a quick review roundup:

My iPhone gets plenty of use at home and on the go, and if the next version only adds in 3G and GPS, I won’t even be all that tempted to upgrade. It’s so much better than any previous phone I’ve owned, and AT&T’s customer service isn’t nearly as bad as it was in the Ameritech/SBC days. I can’t wait till the API is finally released and the 2.0 software comes out. I’m sure my wallet will take a beating that month. Interestingly, I use the iPod functionality less than I did my old iPod.

My Apple TV probably gets the least use of all my gadgets. We have ditched out Netflix subscription (kept forgetting to send movies back), and decided to put that money towards rentals & tv shows. Between the Apple TV and my Xbox 360, we could get pretty much anything except Smallville and NBC shows. Combine this with the rewards my Juniper iTunes card gets for just paying monthly bills with it, and we get quite a deal.

My MacBook is the surprise of the bunch. For many years, I’ve only had 17″ laptops. Unfortunately, the $2,800 Macbook Pro was out of my price range, and there were some great deals on MacBook refurbs, so I picked one up. I was pleasantly surprised that the screen size wasn’t that big of an issue. The real surprise was how quick and light, and long lasting this thing is. Sure, I’m not going to get any modern 3D game to run on it, but it’s almost half the weight of my previous laptop, and the battery life is amazing. I guess the LED backlighting really paid off. I was also able to improve laptop gaming performance on my new laptop. Having a lot of computer geeks friends who love Apple products I can say that the Mac Pro is, well, awesome. It’s great to run three virtual machines, plus all my development servers and software, email and still not break a sweat. It’s replaced three physical machines (dev/file server, photo server, and workstation), so I’m saving on electricity and space. The keyboard is surprisingly awesome to type on, though the Mighty Mouse doesn’t like to always tell the difference between a left and right click.

What’s left to be assimilated? Well, I want to upgrade my wireless network to N, and the likely candidates are an AirPort Extreme and AirPort Express to bridge some HD-capable streaming to my Xbox. My wife likely won’t be switching anytime soon, and is plenty powerful with XP for the internet/email/light web dev she does. Our daughter, however, will probably inherit this MacBook in a few years and eventually get an iPod.

Comments

I’ve given in to WordPress

Yes, I’ve converted my blog over from BlogCFC to WordPress. It was an easy enough process thanks to¬†James Netherton’s migration code.

I also re-organized my posts and categories, slimming down the number of categories I have, and moving most of the metadata over to tags. Hopefully this will help keep things manageable.

Anyone have a good idea of how to get old permalinks to work with WordPress?

Comments

Moving to Mac: SVN, Finale

So, after struggling with all other SVN clients, I’ve settled on a commercial product, Syncro SVN Client. It’s no TortoiseSVN, but it’s got all of the major SVN functionality, it works, and it’s stable. Combine that with the light bit of stuff that I do via Subclipse, and I’ve got all the SVN goodness I need to get along.

I’m still holding out hope for a TortiseSVN port, though.

Comments