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