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#