费了牛劲,终于搞定了,直接贴代码了~~~ 参考了: http://hi.baidu.com/nstarzcm/item/3b0963d65ce411cf1a72b408#a229efd920147e2b10df9bac
<?xml version="1.0" encoding="utf-8"?> <s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="100%" height="100%" xmlns:ns1="*" creationComplete="creationHandler();" title="报表模板上传" close="titlewindow1_closeHandler(event)" xmlns:reporttypewebservice="services.reporttypewebservice.*"> <fx:Script> <![CDATA[ import cn.com.xxxx.event.PagerCloseEvent; import cn.com.xxxx.utils.Constants; import flash.net.FileReference; import flash.net.FileReferenceList; import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.core.mx_internal; import mx.events.CloseEvent; import mx.events.FlexEvent; import mx.managers.PopUpManager; import mx.rpc.AsyncResponder; import mx.rpc.AsyncToken; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import valueObjects.Version; //自定义的关闭事件 private var pagerCloseEvent:PagerCloseEvent = new PagerCloseEvent(); //选择文件 private var selectFileList:FileReferenceList = new FileReferenceList(); //选择的文件的数组 [Bindable] public var selectedFileArray:ArrayCollection = new ArrayCollection(); //当前版本 public var currVersion:Version = new Version(); //当前上传文件的下标 private var currFileIndex:uint = 0; //上传是否已开始 [Bindable] public var uploadStartFlag:Boolean = false; //记录上传成功的function,以备删除 public var lastFunc:Function = null; private function creationHandler():void{ } protected function titlewindow1_closeHandler(event:CloseEvent):void { //上传开始了,不允许关闭 if(!uploadStartFlag) { this.dispatchEvent(pagerCloseEvent); PopUpManager.removePopUp(this); } } //显示行号 private function lfRowNum(item:Object, column:int):String { var index:int=dataGrid.dataProvider.getItemIndex(item) + 1; return String(index); }
protected function button1_clickHandler(event:MouseEvent):void { var fileTypes:FileFilter = new FileFilter("报表模板 (*.xml, *.xsl, *.js)", "*.xml; *.xsl; *.js;"); var allTypes:Array = new Array( fileTypes ); try { selectFileList.browse(allTypes);//不限制类型,就直接browse()即可 selectFileList.addEventListener(Event.SELECT, selectHandler1); } catch (error:Error) { Alert.show("文件选择出现错误,请选择正确的文件"); } } /** * 如果文件被选中,则执行该方法 * */ private function selectHandler1(event:Event):void { //所有文件,放到selectedFileArray中 var upLoadFileList:FileReferenceList = FileReferenceList(event.target); for(var i:int=0;i<upLoadFileList.fileList.length;i++){ var f:FileReference = FileReference(upLoadFileList.fileList[i]); var obj:Object= new Object(); obj.fileName = f.name; obj.fileSize = f.size; obj.fileType = f.type; obj.fileRefrence = f; obj.currValue = 0; //已上传字节数 obj.uploadOk = false; //是否上传完毕 selectedFileArray.addItem(obj); } } /** * 开始上传. * */ protected function doUpload_clickHandler(event:MouseEvent):void { currFileIndex = 0; uploadStartFlag = true; uploadNextFile(); } /** * 上传下一个文件. * */ protected function uploadNextFile():void { if(currFileIndex < selectedFileArray.length) { //已上传OK,跳过 if(selectedFileArray[currFileIndex].uploadOk) { currFileIndex++; uploadNextFile(); } else { //载入文件数据 var upLoadFile:FileReference = FileReference(selectedFileArray[currFileIndex].fileRefrence); upLoadFile.addEventListener(Event.COMPLETE,onLoadComplete); upLoadFile.load(); } }else{ uploadStartFlag = false; Alert.show("上传完毕。"); } } /* * 文件数据载入完毕. */ private function onLoadComplete(e:Event):void { //主动删除监听器 var upLoadFile:FileReference = FileReference(e.target); upLoadFile.removeEventListener(Event.COMPLETE,onLoadComplete); //上传数据 uploadReportTypeFileResult.token = reportTypeWebService.uploadReportTypeFile(currVersion.id,upLoadFile.name,upLoadFile.data); //一定要删除旧的监听器,否则非常混乱 if(uploadReportTypeFileResult.hasEventListener(ResultEvent.RESULT)) { uploadReportTypeFileResult.removeEventListener(ResultEvent.RESULT,lastFunc); } //成功处理 uploadReportTypeFileResult.addEventListener(ResultEvent.RESULT, function(event:ResultEvent):void { //记住该function,以备删除 lastFunc = arguments.callee; //更新当前上传字节数 selectedFileArray[currFileIndex].currValue = upLoadFile.size; selectedFileArray[currFileIndex].uploadOk = true; //下标+1,上传下一个 currFileIndex++; uploadNextFile(); }); //失败处理 uploadReportTypeFileResult.addEventListener(FaultEvent.FAULT, function(info:Object):void { //失败 Alert.show(upLoadFile.name); //下标+1 currFileIndex++; uploadNextFile(); }); } >> </fx:Script> <fx:Declarations> <s:CallResponder id="getReportTypeListResult"/> <reporttypewebservice:ReportTypeWebService id="reportTypeWebService" fault="Alert.show(event.fault.faultString + '\n' + event.fault.faultDetail)" showBusyCursor="true"/> <s:CallResponder id="uploadReportTypeFileResult"/> <!-- 将非可视元素(例如服务、值对象)放在此处 --> </fx:Declarations> <s:VGroup width="100%" height="100%" horizontalAlign="left" textAlign="left"> <s:VGroup width="100%" paddingLeft="10" paddingRight="10" height="305"> <mx:DataGrid width="100%" height="300" id="dataGrid" dataProvider="{selectedFileArray}"> <mx:columns> <mx:DataGridColumn labelFunction="lfRowNum" width="40" headerText="序号"/> <mx:DataGridColumn headerText="文件名称" dataField="fileName" width="300"/> <mx:DataGridColumn headerText="文件大小" dataField="fileSize" width="100"/> <mx:DataGridColumn headerText="文件类型" dataField="fileType" width="100"/> <mx:DataGridColumn width="300" headerText="状态"> <mx:itemRenderer> <fx:Component> <mx:HBox width="100%" paddingLeft="2" horizontalGap="2"> <fx:Script> <![CDATA[ import mx.collections.ArrayCollection; import mx.controls.DataGrid; import mx.controls.Alert; //取消,现在没调用 private function cancel():void{ data.fileRefrence.cancel(); progress.label = "已取消"; } //绑定数据时的方法 override public function set data(value:Object):void{ super.data = value; //一定要留着 //根据currValue显示当前进度 progress.setProgress(data.currValue,data.fileRefrence.size); //旧的监听器没用了,删除 if(data.fileRefrence.hasEventListener(ProgressEvent.PROGRESS)) { data.fileRefrence.removeEventListener(ProgressEvent.PROGRESS,data.func); } //添加新的监听器 data.fileRefrence.addEventListener(ProgressEvent.PROGRESS,progressHandler); data.func = progressHandler; } //监听进度 private function progressHandler(event:ProgressEvent):void{ trace("equal:"+(event.target==data.fileRefrence)); //不是当前文件的进度,忽略掉 if(event.target==data.fileRefrence) { progress.setProgress(event.bytesLoaded,data.fileRefrence.size); data.currValue = event.bytesLoaded; //保存进度 } }
//删除,上传开始后,不让删除,免得下标混乱。 private function deleteItem(data:Object):void { if(!this.outerDocument.uploadStartFlag) { var index:int = this.outerDocument.selectedFileArray.getItemIndex(data); this.outerDocument.selectedFileArray.removeItemAt(index); }else{ Alert.show("已开始上传,不能删除。"); } } >> </fx:Script> <mx:ProgressBar id="progress" width="100%" label="%3%%" mode="manual" minimum="0" maximum="{data.fileRefrence.size}" labelPlacement="center"> </mx:ProgressBar> <mx:LinkButton width="20" click="deleteItem(data as Object)" toolTip="从列表中删除" icon="@Embed('assets/delete.gif')"> </mx:LinkButton> </mx:HBox> </fx:Component> </mx:itemRenderer> </mx:DataGridColumn> </mx:columns> </mx:DataGrid></s:VGroup> <s:HGroup width="95%" verticalAlign="middle"> <mx:Spacer width="50%"/> <s:Button label="选择文件" click="button1_clickHandler(event)"/> <s:Button id="clearListBtn" label="清空列表" click="selectedFileArray.removeAll()" enabled="{(selectedFileArray.length>0) && (uploadStartFlag==false)}"/> <s:Button id="uploadBtn" label="开始上传" click="doUpload_clickHandler(event)" enabled="{(selectedFileArray.length>0) && (uploadStartFlag==false)}"/> <s:Button id="closeBtn" label="关闭" click="{dispatchEvent(new CloseEvent(CloseEvent.CLOSE))}" enabled="{uploadStartFlag==false}"/> </s:HGroup> <s:HGroup width="50%" verticalAlign="middle" height="5"> </s:HGroup> </s:VGroup> </s:TitleWindow>
服务端是Web Service,大概就这么几行:
public void uploadReportTypeFile(String versionId, String fileName, byte[] fileData) throws CommonException { System.out.println(fileName); }
和普通的调用一样,只是传了个byte[]而已。
以下是问题的根源,比较乱,写不明白了,供参考吧: 另有一个参考: 文件上传与进度条监控的烦人问题http://hi.baidu.com/nstarzcm/item/40d812083ee369dcdde5b030 -------------------------------------------------------------- 比如11条记录,初始显示1-10,那么会执行1-10行数据的set data方法,11的不会执行。 向下滚动一行,会执行11的set data方法,其它的不会执行。此时1行数据隐藏了。 向上滚动一行,会执行1的set data方法,其它的不会执行,此时11行数据隐藏了。 也就是,itemRenderer对象是很少的,要显示哪行数据时,就随便拿一个闲置的itemRenderer,往里set data。 结果就是,一个itemRenderer对象是N多行共用,想用itemRenderer来保持一行数据的状态是不行的了。 所以,还是把状态数据,放到data本身里面吧,然后set data时现更新控件什么的。 烦~~~
下面是关于上传进度条的混乱~~ ===================================================================================== 比如共30条数据,初始显示1到10. itemRenderer的set data(): ------------------------------------------------------------ 文件1---->进度条1,文件1添加监挺气,文件1的上传字节数传递给进度条1, 文件2---->进度条2, 文件3---->进度条3, ... 文件10---->进度条10 ------------------------------------------------------------ (1)先不动滚动条: 进度条1-->进度条10,显示正常。 但是,11-30行数据,由于没有显示,所以根本没有执行set data方法。 在监挺气里,更新data行的currValue就不会执行了。 所以,在上传OK的代码里,直接更新了data行里的currValue字段为文件size。 这样,当滚动条拉下来时,进度条直接显示currValue,也就是100%了。
如果,滚动条拉下来时,文件没上传完毕,也没关系。 因为,这时,set data方法执行了,监挺气被添加了,它会更新currValue。
唯一可能的问题点是: 滚动条拉下来后,开始是显示0%的,监挺气被添加之前的一瞬间,上传完毕了。 监挺气被添加上,也没用了,不会触发了。所以进度条此时就显示0%,但实际上是100%。 上下拉拉滚动条,让该行隐藏再显示,就正常了。小问题,不管啦。
(2)还有一个问题, 假设上传开始后,向下滚动了滚动条,显示11-20行~~ ------------------------------------------------------------ 文件11---->进度条1,文件11添加监挺气,文件1的上传字节数传递给进度条1, 文件12---->进度条2, 文件13---->进度条3, ... 文件20---->进度条10 ------------------------------------------------------------ 注意,进度条还是1-10,因为进度条是itemRenderer内的,itemRenderer是重用的。
这时,文件1的字节数会传给进度条1,文件11的字节数也传给进度条1. 所以,进度条显示会很混乱。 需要判断event.target与当前行的文件是一致的,才更新进度。 如文件1的字节数过来,就忽略,文件11的字节数过来才更新。
所以,实际上,此时,文件1的进度是无人接收的。 好在,当你想看文件1时,滚动条拉过去,set data执行, 如果已完成,就是100%了,没完成,则会添加监挺气,有进度条监听进度了,就OK了。 只是,进度可能会跳跃一下。
开始显示1-10,然后显示11-20,然后显示1-10,那么文件1实际上就有两个监挺气了。 实际上,之前那个监挺气没什么用了,因为它的监听者现在在为其它行服务, 所以,删掉它即可。 ------------------------------------------------------------------- Flex全都是异步调用,全都是事件触发,好烦啊~~
如果你的事件很乱,那么一定是触发乱了。
添加触发之前,一定看看有没有旧的触发,删掉它,否则后患无穷~~切记~~ ----------------------------------------------------------------------
|