swfupload
前言
最近项目中要求使用HTTP做文件上传,而且要求有进度显示,在网上东找西寻了半天,解决方案倒也不少,比如使用Ajax上传,但感觉这种方式的进度提示太麻烦,所以没有采用,后来看到了SWFUpload,就找了些资料来看,觉得符合自己的要求,研究了足有两天,略有心得,忙不迭地记录下来,以防止时间长了遗忘,如果不小心帮助了别人,那就更好了,呵呵...
下面摘抄一段SWFUpload官网的介绍(别人翻译过来的):
SWFUpload 最初是由Vinterwebb.se 开发的客户端文件上传工具。它结合JavaScript和flash在浏览器中提供一个优于传统上传标签 的功能(和良好的用户体验)。
SWFUpload 的主要特性:
- 文件浏览对话框中可以选择多个文件
 - AJAX风格的上传,不用重刷新
 - 上传过程中的各种事件.
 - 可以在客户端调节图片大小
 - 它使用的类命名空间兼容各种js库(i.e., jQuery, Prototype, 等.).
 - 支持 Flash 9 and Flash 10 (2.2.0版本后取消对flash 8的支持)
 
SWFUpload 的设计理念与其他基于flash的上传工具不同。SWFUpload 给开发者尽可能多的UI控制能力. 开发者可以使用 XHTML, CSS, javascript 来使它更符合自己网站的样式风格. 它提供一组简单的js事件更新上传状态,开发者可以根据这些事件来在网页中显示文件上传进度
好了,夸奖的话不多说了,既然它这么受欢迎,想必是有一定优势的。
下面记录一下我做的一个小示例,先预览一下程序运行效果:

有朋友索要例子源码,其实我早已放在我的资源里了,并没有设置下载积分,有需要的朋友请到这里下载:http://download.csdn.net/detail/zhangyihui1986/4538748
http://code.google.com/p/swfupload/ ,请自行下载,下载的压缩文件中含有一个文档,里面详细介绍了SWFUpload的配置参数、事件支持、支持方法等。
这里是官网上的几个例子:http://demo.swfupload.org/
网络上关于SWFUpload的博客资源很多,但很多博客质量不太高,而且转来转去,内容重复,所以我在查资料的时候没有找到太合适的资源,后来找到了这里:http://webdeveloperplus.com/jquery/multiple-file-upload-with-progress-bar-using-jquery/ ,这是一个外国人写的关于SWFUpload的小教程,UI做地也挺好,于是我就参照着做了个小例子。
不过,上面链接的教程是针对SWFUpload一个针对jQuery的插件写的,这个插件地址为:http://blogs.bigfish.tv/adam/2009/06/14/swfupload-jquery-plugin/,该插件好像仅仅是完善了一下SWFUpload的事件机制,使用它可采用jquery的链式写法,但我没有用它,而是用的原生的SWFUpload2.2版本。
view plaincopy
 
 
 
- <link href="<%=path %>/js/ligerUI/skins/Aqua/css/ligerui-all.css" type="text/css" rel="stylesheet" />  
 
- <script type="text/javascript" src="<%=path %>/js/jquery-1.7.2.min.js">script>  
 
- <script type="text/javascript" src="<%=path %>/js/ligerUI/js/core/base.js">script>  
 
- <script type="text/javascript" src="<%=path %>/js/ligerUI/js/plugins/ligerDrag.js">script>  
 
- <script type="text/javascript" src="<%=path %>/js/ligerUI/js/plugins/ligerDialog.js">script>  
 
- <script type="text/javascript" src="<%=path %>/js/swfupload/swfupload.js">script>  
 
- <script type="text/javascript" src="<%=path %>/js/swfupload/plugins/swfupload.queue.js">script>  
 
在此处我用到了ligerUI框架,如果不使用它,那么仅需要引jquery和swfupload.js两个文件即可。
3)JSP文件中HTML结构
[html] view plaincopy- <div id="swfupload">
 - <span id="spanButtonPlaceholder">span>
 - <p id="queueStatus">p>
 - <ol id="logList">ol>
 - div>
 
 
4)JSP文件中CSS,给文件上传进度列表赋与样式,我对CSS不够熟悉,此处的样式几乎全部来自于前面那个教程,仅作了一些小的变动。如果您觉得这还不够好,那您自己来实现漂亮的UI界面吧!
- #logList { margin: 0; padding: 0; width: 500px }
 - #logList li { list-style-position: inside; margin: 2px; border: 1px solid #ccc; padding: 10px; color: #333;
 - font-size: 15px; background: #FFF; position: relative; }
 - #logList li .progressBar { border:1px solid #333; height:5px; background:#fff; }
 - #logList li .progressValue { color: red; margin-left: 5px }
 - #logList li .progress { background:#999; width:0%; height:5px; }
 - #logList li p { margin:0; line-height:18px; }
 - #logList li.success { border:1px solid #339933; background:#ccf9b9; }
 - #logList li span.cancel { background:url('../images/delete.gif') no-repeat; position:absolute; top:5px;
 - right:5px; width:16px; height:16px; cursor:pointer }
 
5)JSP中的Javascript,主要是创建SWFUpload组件实例,并绑定监听函数,在监听函数中处理进度提示:
- var contextPath;
 - var queueErrorArray;
 - $(function(){
 - contextPath = $("#contextPath").val();
 - var swfUpload = new SWFUpload({
 - upload_url: contextPath + '/swfupload/upload!upload.action',
 - flash_url: contextPath + '/js/swfupload/Flash/swfupload.swf',
 - file_post_name: 'fileData',
 - use_query_string: true,
 - post_params: {
 - param1: 'Hello',
 - param2: encodeURI('你好',"utf-8")
 - },
 - file_types: "*.rar;*.zip",
 - file_types_description: '上报数据文件',
 - file_size_limit: '102400',
 - // file_upload_limit: 5,
 - file_queue_limit: 3,
 - // handlers
 - file_dialog_start_handler: fileDialogStart,
 - file_queued_handler: fileQueued,
 - file_queue_error_handler: fileQueueError,
 - file_dialog_complete_handler: fileDialogComplete,
 - upload_start_handler: uploadStart,
 - upload_progress_handler: uploadProgress,
 - upload_success_handler: uploadSuccess,
 - upload_complete_handler: uploadComplete,
 - button_placeholder_id: 'spanButtonPlaceholder',
 - button_text: '选择文件',
 - button_text_style: '.whiteFont{ color: #FFFFFF; }',
 - button_text_left_padding: 40,
 - button_text_top_padding: 6,
 - button_image_url: contextPath + '/images/button.png',
 - button_width: 133,
 - button_height: 33,
 - button_cursor: SWFUpload.CURSOR.HAND,
 - button_window_mode: SWFUpload.WINDOW_MODE.TRANSPARENT,
 - debug: false,
 - custom_settings: {}
 - });
 - });
 - //======================================== 回调函数Handlers ===================================
 - /**
 - * 打开文件选择对话框时响应
 - */
 - function fileDialogStart() {
 - if (queueErrorArray) {
 - queueErrorArray = null;
 - }
 - }
 - /**
 - * 文件被加入上传队列时的回调函数,增加文件信息到列表并自动开始上传.
 - *
 - * SWFUpload.startUpload(file_id)方法导致指定文件开始上传,
 - * 如果参数为空,则默认上传队列第一个文件;
 - * SWFUpload.cancelUpload(file_id,trigger_error_event)取消指定文件上传并从队列删除,
 - * 如果file_id为空,则删除队列第一个文件,trigger_error_event表示是否触发uploadError事件.
 - * @param file 加入队列的文件
 - */
 - function fileQueued(file) {
 - var swfUpload = this;
 -     var listItem = '
- '">';
  - listItem += '文件:' + file.name + '(' + Math.round(file.size/1024) + ' KB)';
 - listItem += ''
 -               + ''
 -               + '
Pending
' - + ' '
 - + ' ';
 - $("#logList").append(listItem);
 - $("li#" + file.id + " .cancel").click(function(e) {
 - swfUpload.cancelUpload(file.id);
 - $("li#" + file.id).slideUp('fast');
 - })
 - // swfUpload.startUpload();
 - }
 - /**
 - * 文件加入上传队列失败时触发,触发原因包括:
 - * 文件大小超出限制
 - * 文件类型不符合
 - * 上传队列数量限制超出等.
 - * @param file 当前文件
 - * @param errorCode 错误代码(参考SWFUpload.QUEUE_ERROR常量)
 - * @param message 错误信息
 - */
 - function fileQueueError(file,errorCode,message) {
 - if (errorCode == SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED) {
 - alert("上传队列中最多只能有3个文件等待上传.");
 - return;
 - }
 - if (!queueErrorArray) {
 - queueErrorArray = [];
 - }
 - var errorFile = {
 - file: file,
 - code: errorCode,
 - error: ''
 - };
 - switch (errorCode) {
 - case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT:
 - errorFile.error = '文件大小超出限制.';
 - break;
 - case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE:
 - errorFile.error = '文件类型受限.';
 - break;
 - case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE:
 - errorFile.error = '文件为空文件.';
 - break;
 - default:
 - alert('加载入队列出错.');
 - break;
 - }
 - queueErrorArray.push(errorFile);
 - }
 - /**
 - * 选择文件对话框关闭时触发,报告所选文件个数、加入上传队列文件数及上传队列文件总数
 - * @param numSelected 选择的文件数目
 - * @param numQueued 加入队列的文件数目
 - * @param numTotalInQueued 上传文件队列中文件总数
 - */
 - function fileDialogComplete(numSelected,numQueued,numTotalInQueued) {
 - var swfupload = this;
 - if (queueErrorArray && queueErrorArray.length) {
 -         var table = $('
');文件 大小  - for(var i in queueErrorArray) {
 -             var tr = $('
');  -             var info = '
' + queueErrorArray[i].file.name + '(' '- + queueErrorArray[i].error + ')
  -                         + '
' + queueErrorArray[i].file.size + 'bytes '; - table.append(tr.append(info));
 - }
 - $.ligerDialog.open({
 - width: 500,
 - content: table,
 - title: '文件选择错误提示',
 - buttons: [{
 - text: '确定',
 - onclick: function(btn,dialog,index) {
 - $("#queueStatus").text('选择文件: ' + numSelected
 - + ' / 加入队列文件: ' + numQueued);
 - swfupload.startUpload();
 - dialog.close();
 - }
 - }]
 - });
 - queueErrorArray = [];
 - } else {
 - this.startUpload();
 - }
 - }
 - /**
 - * 文件开始上传时触发
 - * @param file 开始上传目标文件
 - */
 - function uploadStart(file) {
 - if (file) {
 - $("#logList li#" + file.id).find('p.status').text('上传中...');
 - $("#logList li#" + file.id).find('p.progressValue').text('0%');
 - }
 - }
 - /**
 - * 文件上传过程中定时触发,更新进度显示
 - * @param file 上传的文件
 - * @param bytesCompleted 已上传大小
 - * @param bytesTotal 文件总大小
 - */
 - function uploadProgress(file,bytesCompleted,bytesTotal) {
 - var percentage = Math.round((bytesCompleted / bytesTotal) * 100);
 - $("#logList li#" + file.id).find('div.progress').css('width',percentage + '%');
 - $("#logList li#" + file.id).find('span.progressValue').text(percentage + '%');
 - }
 - /**
 - * 文件上传完毕并且服务器返回200状态码时触发,此时文件的上传周期并未完成,
 - * 不能在此事件监听函数开始下一个文件的上传
 - * @param file 上传的文件
 - * @param serverData 服务器在执行完接收文件方法后返回的数据
 - * @param response Boolean类型,表示是否服务器返回数据
 - */
 - function uploadSuccess(file,serverData,response) {
 - var item = $("#logList li#" + file.id);
 - item.find('div.progress').css('width','100%');
 - item.find('span.progressValue').css('color','blue').text('100%');
 - item.addClass('success').find('p.status').html('上传完成!');
 - }
 - /**
 - * 在一个上传周期结束后触发(uploadError及uploadSuccess均触发)
 - * 在此可以开始下一个文件上传(通过上传组件的uploadStart()方法)
 - * @param file 上传完成的文件对象
 - */
 - function uploadComplete(file) {
 - this.uploadStart();
 - }
 
view plaincopy
 
 
 
- var swfUpload = new SWFUpload({settings});  
 
我们需要传给它一个配置对象,这个配置对象的内容很多,详细介绍请参照使用说明文档,在此仅介绍几个重要一些的配置参数,以注释的形式写在代码里。
[javascript] view plaincopy- upload_url: '', // 上传操作后台处理URL,相当于form的action属性
 - flash_url: '', // swf文件的位置,指向JS目录下的swfupload.swf文件即可
 - file_post_name: '', // 提交到后台的文件的名字,相当于域的name值,默认为“Filedata”
 - file_types: "*.rar;*.zip", // 可上传的文件类型
 - file_types_description: '', // 可上传文件类型的描述信息
 - file_size_limit: '', // 上传文件大小限制,接受值和单位,默认单位是KB,如'1024 MB'
 - button_placeholder_id: 'spanButtonPlaceholder', // 设置一个HTML元素,用以渲染Flash的Button
 - button_image_url: '', // 按钮图片,Flash使用,可以有多种状态(mouseout、hover等)
 - button_width: 270, // 按钮的宽,必须要设置,不设置按钮无法显示
 - button_height: 20, // 按钮的高,必须要设置,不设置按钮无法显示
 - button_cursor: SWFUpload.CURSOR.HAND, // 鼠标移到按钮上的光标样式
 - button_window_mode: SWFUpload.WINDOW_MODE.TRANSPARENT, // Flash剪辑的WMODE属性
 
需要注意的是按钮的高和宽一定要指定,否则flash无法显示。
view plaincopy
 
 
 
- // handlers:事件监听函数,请参考使用说明文档  
 
- file_dialog_start_handler: fileDialogStart,// 文件选择对话框打开时触发,传入SWFUpload定义的File参数  
 
- file_queued_handler: fileQueued,// 文件被加入队列时触发  
 
- file_queue_error_handler: fileQueueError,// 文件加入队列出错时触发,包括大小限制,类型限制,空文件等均会触发  
 
- file_dialog_complete_handler: fileDialogComplete,// 文件选择对话框在文件选择完成并关闭时触发  
 
- upload_start_handler: uploadStart,// 文件开始上传时触发  
 
- upload_progress_handler: uploadProgress,// 上传过程中定时触发,此方法在更新进度条时比较重要  
 
- upload_success_handler: uploadSuccess,// 文件上传成功时触发  
 
- upload_complete_handler: uploadComplete,// 文件上传完成时触发,包括上传成功与上传失败,此方法可以开始下一文件的上传  
 
view plaincopy
 
 
 
- this.startUpload();  
 
view plaincopy
 
 
 
- this.startUpload();  
 
- this.startUpload();
 
view plaincopy
 
 
 
- this.startUpload();  
 
因为upload_complete_handler事件是在一个文件上传后触发,不管该文件是否上传成功都会触发该事件,所以我们在这里再调用一次startUpload方法是合适的,这样SWFUpload组件在前一个文件上传完成后就会自动开始下一个文件的上传。
view plaincopy
 
 
 
- use_query_string: true,  
 
- post_params: {  
 
-     param1: 'Hello',  
 
-     param2: '你好'  
 
- }  
 
但是如果参数值中含有中文的话,那么后台会报错,也取不到值,可以这样解决:
在JS中先用UTF-8进行中文编码
[javascript] view plaincopy- use_query_string: true,
 - post_params: {
 - remark: encodeURI('中文',"utf-8")
 - }
 
然后在后台再转回来,在Java中就体现为
[java] view plaincopy- URLDecoder.decode(request.getParameter("remark"), "utf-8");
 
如此便可解决中文参数传递问题。
view plaincopy
 
 
 
- uploadSuccess(file object, server data, received response)  
 
这个事件发生后会传三个参数到我们定义的监听函数中,第一个是上传完成的文件对象(关于文件对象可参看SWFUpload的文档,其实就是一个JS对象,包含一些重要的文件信息),第二个其实是服务器返回的数据,如果后台用的直接跳转,那么这里的server data就会是跳转页面的HTML结构,document.write()将其输出,也算是完成跳转了吧!如果你仅仅是向前台输出个字符串,并没有跳转,那么这里就应该是你输出的字符串,在这里就可以用location.href=“来实现跳转;第三个参数是Boolean类型,表示是否接收到服务器传回的数据。
需要注意的是该事件在每个文件上传成功均会触发,如果同时在上传多个文件,那么第一个文件上传完成后页面就直接跳转了,后台的文件也就得不到机会上传了,所以这里需要判断一下队列里是否还有没有上传完成的文件,如果没有了再跳转,就可以了,至于如何判断队列里是否还有未上传的文件,这里用到了SWFUpload的另一个对象Stats。该对象提供了上传队列的状态信息,访问实例的getStats方法可获取此对象,该对象中有一些属性,其中有一个files_queued属性可以表示是否还有未上传的文件,如果该属性为0则表示全部上传,可以这样做
[javascript] view plaincopy- if(this.getStats().files_queued = 0) {
 - // jump code
 - }
 
PHP and ASP.net
也就是说非IE浏览器下Flash Player插件发送的也是IE浏览器当前页面的cookie,并且Session是靠Cookie中保存的SessionId实现的,因此后台处理程序另新建了Session,程序也就出现了上述错误。
找到的解决办法是手动将SessionID传到后台服务端。
在上传路径URL里加上jsessionid变量即可,如下:
[javascript] view plaincopy- upload_url: contextPath + '/web/user-upload!upload.action;jsessionid=<%=request.getSession().getId() %>'
 
这样就可以解决问题了,有人还说可以使用SWFUpload组件的一个插件:swfupload.cookies.js,但我没有弄好,看它的源码是分析浏览器的cookie然后将它们拼接到post_params配置项中,我手动拼上jsessionid也是不起作用,不知道是不是自己没弄对,反正是这个插件我没有成功使用。
原文:http://blog.csdn.net/zhyh1986/article/details/7926166#