引用:http://tommy---2005.iteye.com/blog/725641
笔者在网上查找流行的上传组件,swfupload引入眼帘,受到JavaEye的一篇文章启发,历时三天,加以研究,现将心得奉上,献礼JavaEye。
由于笔者才疏学浅,经验匮乏,介绍不深入,仅供菜鸟参考,还望高手赐教。
一、准备工作
从官网上http://www.swfupload.org/下载发布版v2.2.0.1,仅取下载文件中的SWFUpload.js和swfupload.swf即可,另外可利用官网上的DEMO,从中获取一张PNG图片。
swfupload.swf是上传组件的核心,一个特制的FLASH,具有浏览文件,上传文件的功能,以按钮形式体现在用户眼前,上文中提及的PNG图片浮在按钮上,可增强视觉效果。SWFUpload.js与swfupload.swf交互,向开发者提供操作接口。
文档中指出,SWFUpload并不是拖放式的上传控件,它需要使用者具备JavaScript(以下简称JS)和DOM的知识,进行UI界面的设计。
二、使用步骤笔者借助J2EE平台实例,步步为营,介绍其使用方法。
1.部署组件
将swfupload.swf、SWFUpload.js和upload.png图片(上文提及的图片)正确放置于WEB项目中,并且新建upload.jsp文件与前三者置于同一目录中。下图中tryswfupload为WEB项目名称。
在upload.jsp中引入swfupload.js组件:
<script type="text/javascript" src="swfupload.js"></script>
2.实例化swfupload组件
实例化swfupload组件的任务就是正确配置参数,方法是编写JS代码,目前急需配置两方面内容:一是指定swfupload.swf的物理位置;二是准确配置flash中的上传按钮。
button_placeholder_id属性的值spanButtonPlaceholder为上传按钮的ID值,因此可以显示在JSP中。读者须注意,按钮的高和宽一定要指定,否则flash无法显示。至此,JSP页面已经实现用户选择文件的功能。
读者不妨试试,便会看到运行效果图。
点击“选择文件”按钮,文件对话框即刻跳出,用户选择多个文件后,对话框立即关闭,选择的文件进入排队序列,等待上传。当调用SWFUpload 对象的startUpload方法执行上传命令。在此例中,swfu已经实例化为SWFUpload 对象,故可以在JSP中添加一个按钮,在其点击事件中调其该方法。在JSP中添加代码:
<button onclick="swfu.startUpload()">上传</button>
虽如此,但文件没有上传到服务器上,至少客户端不知道上传的目的地。
3.指定客户端将上传信息发送至服务器的URL——struts2登场
swfupload将upload_url属性值作为客户端将文件上传请求信息发送至服务器的URL,读者可理解为struts2表单中的action属性值,upload_url属性值默认为web项目的主页。笔者仅介绍struts2如何接收请求。
struts2已经实现了上传功能,笔者不作详细介绍(由于struts2实现了无缝整合,开发者仅需要将上传的临时进行复制即可,其使用方法易于掌握)。
使用struts2的上传功能,开发者需要知道file标签的name属性值,在此基础上,编写action类即可,并在struts.xml文件中配置名字其名字。在swfupload中,file标签的name属性等价为file_post_name属性,其默认值为Filedata。至此,具备了URL和name属性值,便可开始struts2整合之旅。
首先将action的名字赋值给 swfupload中upload_url属性。接着编写自己的action类,类中包含两个属性Filedata和Filename,Filedata和file_post_name值相同(文档推荐使用默认值Filedata)。 Filedata指向了struts2上传的临时文件路径(action中的execute函数执行完毕,临时文件将被删除),Filename为上传文件的名字。Execute函数实现将临时文件复制到指定的目录(此例将它放在WEB项目下bin目录下的upload中)。此例action的名字为“upload”,在JSP中将upload_url属性赋值为upload。
4.添加捕获事件函数
swfupload仅实现了后台操作,前台的处理空间留给了开发者,它采用事件触发机制,让开发者捕获特定事件,并鼓励开发者自定义对应的事件处理函数(笔者定义为:捕获事件函数)进行相应处理。即当swfupload内部某一特定事件发生,便触发JS函数,JS函数通过回调机制将函数参数继续传入自定义的JS函数中。Swfupload通过固定的事件函数名属性值寻找自定义的JS函数,所以在初始化工作中,将自定义的JS函数名赋值给swfupload指定的对应属性即可。
比如,当你选择上传文件后,文件对话框随即关闭,产生关闭对话框完成的事件,内置的fileDialogComplete函数被触发,函数执行完必要的操作后,将整个参数信息传入file_dialog_complete_handler属性值对应的JS函数。所以,开发者仅需将自定义的JS函数名赋值给file_dialog_complete_handler属性即可。Swfupload向外提供的所有事件以及对应的函数定义,文档有详细说明。笔者将“添加捕获XXX事件函数”定义为:自定义JS函数用来捕捉XXX事件,XXX表示swfupload内部捕获事件函数,并函数名赋值给XXX事件对应的属性。比如“添加捕获fileDialogComplete事件函数”表示先自定义JS函数(假设函数名为fileDialogCompleteHandler),用来捕获fileDialogComplete事件,并且将fileDialogCompleteHandler赋值给fileDialogComplete事件对应的属性file_dialog_complete_handler。
为便于管理,笔者建议新建一个JS文件,专门用来存放捕获事件函数,此例为handler.js,注意在JSP中要将其引入。
<script type="text/javascript" src="handler.js"></script>
5.实现批量上传
swfupload不自动批量上传,读者可以尝试选择两个文件点击上传按钮,在服务器端却仅有一个文件,当再次点击上传按钮后,服务器端又多出一个文件。Swfupload虽支持批量上传,但本质仍是单个文件依次上传,这有别于传统设计模式,但其益处却避免了开发者编写大量代码,迭代分析所选文件。
文档指出,添加捕获uploadComplete事件函数(其对应属性为upload_complete_handler),并在其中调用上传函数startUpload,通过递归的方式实现批量上传,即在某个文件上传完成后,再次启动文件上传。此例,该函数被定义为uploadComplete。
在JSP中添加upload_complete_handler属性并赋值为uploadComplete。
6.显示上传文件列表
显示出上传文件列表能够增强用户体验,因为用户将看见选择的文件信息。下文介绍将选择的文件以表格形式显示出来,每行内容为依次为文件名、大小、状态(QUEUED、ERROR、COMPLETE)。
读者不妨在文档中仔细查找是否存在其参数包含file类型集合的API函数,其结果必然徒劳,因为只有参数为file类型的API函数。这在上文已有介绍:swfupload本质依靠单个文件形式上传。该知识点对于对于灵活掌握swfupload举足轻重。故当选择某一文件后,在表格追加显示该文件信息,在文件对话框关闭后,显示表格(表格初始状态为隐藏,在JSP中定义)。
fileDialogComplete事件在选择上传文件后产生,其对应的事件属性为file_dialog_complete_handler,对应的内置函数为fileDialogComplete(number of files selected, number of files queued, total number of files in the queued),虽然三个整型参数无法提供所选文件具体信息,但可实现让隐藏的表格显示出来。添加捕获fileDialogComplete事件函数。
在JSP中添加file_dialog_complete_handler属性并赋值为fileDialogComplete。
在fileDialogComplete事件产生前, fileQueued事件已经发生。fileQueued事件表示所选文件进入上传排队序列,对应的事件属性为file_queued_handler,对应的内置函数为fileQueued(file object),它的发生频率取决于所选文件的个数。通过添加捕获fileQueued事件函数,可实现文件列表动态显示。
fileQueued函数配合状态转换函数showStatus动态添加表格行,其中在showStatus函数中,SWFUpload是全局对象,它包含一些只读对象,文档中有说明,此例仅使用FILE_STATUS常量。
在JSP中添加file_queued_handler属性并赋值为fileQueued。
表格行能动态生成,但其中的状态字段仍然保持最初状态QUEUED,无法动态体现文件上传后的实际状态(ERROR、COMPLETE)。笔者的方法是通过row.id = file.id将文件和表格的行绑定,根据文件定位其所属表格行,由此改变状态单元格数据。
上传错误将发生uploadError事件,属性为upload_error_handler,内置函数为uploadError(file object, error code, message);上传成功将发生uploadSuccess事件,属性为 upload_success_handler,内置函数为uploadSuccess(file object, server data, received response)。分别添加这两个捕获事件函数,在函数内文件取出状态,动态改变状态字段值。
在JSP中添加upload_error_handler属性并赋值为uploadError,添加upload_success_handler属性并赋值为uploadSuccess。
至此,JSP中SWFUpload的实例化代码为:
三、从页面跳转到请求流程
此时,本应划上句号,因为所选文件已经上传到upload目录下,但笔者还是想就部分朋友反映“上传完成后,页面不跳转”现象谈谈个人观点。页面不跳转是一个不争的事实,你大可反复尝试。难道这意味着struts2页面跳转功能失效呢?笔者在没搞清楚swfupload原理前,也为之困扰。struts2页面跳转功能仍然良好地在运转,不过它将跳转的页面回响至swfupload,swfupload用内置函数uploadSuccess捕获。对此,理解用户从访问上传页面到页面跳转的整个请求过程至关重要。
首先浏览器访问WEB服务器,打开上传页面,这个过程和打开网页原理相同。当点击“选择文件”按钮后,浏览器通过JS启用swfupload后退出舞台,flash登场。当点击“上传”按钮,flash模拟浏览器向WEB服务器发起连接(新开session),向WEB服务器发送上传信息。可见,当WEB服务器返回信息,目的地不是浏览器,而是flash。整个过程,flash始终和JS紧密交融,将发生的事件通知JS,给开发者留下了足够的编程空间。这不得不赞叹swfupload,将flash设计得淋漓尽致,将JS、WEB服务器、浏览器天衣无缝融入一体。
当WEB服务器响应代码为200时(swfupload表示上传成功的默认代码,可通过修改http_success属性值改变),swfupload便产生上传成功事件,故可通过添加捕获上传成功事件函数得到struts2返回的页面,但不可通过uploadError事件处理struts2拦截器抛出的异常信息,因为swfupload无法解析WEB服务器返回的信息,它仅靠WEB服务器返回代码产生相应事件。
上传成功事件对应的内置函数为uploadSuccess(file object, server data, received response),其中第一个参数为上传的文件,第二个参数为服务器返回的数据,第三个参数为布尔值,从字面上看,估计表示是否收到信息。struts2跳转的页面以HTML代码形式赋值给第二个参数,故可在该捕获事件函数中,置入document.write(server_data)语句,实现页面跳转。
笔者认为swfupload并不希望开发者这样做,因为单个文件的上传机制将导致出现上传完一个文件页面就跳转一次的荒唐现象。其实,只要任何时候调用document.write方法,swfupload立即失效,切忌这样做。
对于实现批量上传成功页面跳转的方法,笔者建议可将server_data缓存,在捕获uploadSuccess事件函数中,如果上传队列为空(getStats().files_queued > 0),再用document.write向swfupload say Goodbye。