SpringMVC+SwfUpload进行多文件同时上传
由于最近项目需要做一个多文件同时上传的功能,所以好好的看了一下各种上传工具,感觉uploadify和SwfUpload的功能都比较强大,并且使用起来也很方便。SWFUpload是一个flash和js相结合而成的文件上传插件,而且该插件可以用在php、.net、Java等项目开发中。在使用的过程中需要引入两个js文件,并且进行相关参数的配置,同时也可以定义一系列的事件,如:上传成功事件,上传失败事件,正在上传事件等等。由于我在项目开发是使用SpringMVC进行的,所以下面我根据我自己的项目简述一下SwfUpload如何整合到Web项目中去。
首先说一下Swfupload相对于HTML中file标签上传的优点:
- 允许一次上传多个文件,但会有一个上传队列,队列里文件的上传是逐个进行的,服务器端接收文件时跟普通的表单上传文件是一样的(相当于表单一次提交了一个file,但是提交了多次,后台处理的时候只需要处理一次即可);
- 用flash进行上传,页面无刷新,且可自定义Flash按钮的样式, 类似AJAX的无刷新上传,里面可以引入swf文件实现动态效果;
- 可以显示上传进度,也可以在浏览器端就对要上传的文件格式,大小等进行配置(只需要配置一个参数即可,比用js的数据验证要好很多);
- 良好的浏览器兼容性并且兼容其他JavaScript库 (例如:jQuery,EXT, Prototype等);一般含有flash插件的即可正常运行,但是好像最低要求flash的版本要达到flash9;
- SwfUpload还提供了丰富的事件接口供开发者使用;开发者可以自定义各种和上传相关的事件;
SWfUpload实现文件的上传流程如下:
1、引入相应的js文件 (swfupload.js和swfupload.queue.js必须引入)
2、实例化SWFUpload对象,传入一个配置参数对象进行各方面的配置。
3、点击SWFUpload提供的Flash按钮(也可以更改配置信息中的button_image_url参数来配置自己的按钮),弹出文件选取窗口选择要上传的文件;
4、文件选取完成后符合规定的文件会被添加到上传的队列里;
5、调用startUpload方法让队列里文件开始上传;
6、文件上传过程中会触发相应的事件(想要触发什么事件需要自己定义,但是一般网上搜一下就可以了,大部分定义都相同,我也会贴出我的代码),开发者利用这些事件来更新ui、处理错误、发出提示等等;
下面说我的项目中SpringMVC+SwfUpload的配置过程及代码:
引入相应的JS文件后实例化SwfUpload对象(JS代码,一般嵌入到jsp文件中或者单独写一个文件引入到jsp页面内):
1 var swfu; 2 3 window.onload = function() { 4 var settings = { 5 flash_url : "swfupload/swfupload.swf", 6 flash9_url : "swfupload/swfupload_fp9.swf", 7 upload_url: "http://localhost:8080/ams/upload/fileUpload.do", 8 post_params: {"PHPSESSID" : "aa"}, 9 file_size_limit : "100 MB", 10 file_types : "*.*", 11 file_post_name : "filedata", 12 file_types_description : "All Files", 13 file_upload_limit : 100, 14 file_queue_limit : 0, 15 custom_settings : { 16 progressTarget : "fsUploadProgress", 17 cancelButtonId : "btnCancel" 18 }, 19 debug: true, 20 21 // Button settings 22 button_image_url: "images/TestImageNoText_65x29.png", 23 button_width: "65", 24 button_height: "29", 25 button_placeholder_id: "spanButtonPlaceHolder", 26 button_text: '<span class="theFont">Hello</span>', 27 button_text_style: ".theFont { font-size: 16; }", 28 button_text_left_padding: 12, 29 button_text_top_padding: 3, 30 31 // The event handler functions are defined in handlers.js 32 swfupload_preload_handler : preLoad, 33 swfupload_load_failed_handler : loadFailed, 34 file_queued_handler : fileQueued, 35 file_queue_error_handler : fileQueueError, 36 upload_start_handler : uploadStart, 37 upload_progress_handler : uploadProgress, 38 upload_error_handler : uploadError, 39 upload_success_handler : uploadSuccess 40 }; 41 42 swfu = new SWFUpload(settings); 43 };
编写相关的监听事件(JS代码,即html引入的handler.js文件):
1 function preLoad() { 2 if (!this.support.loading) { 3 alert("You need the Flash Player 9.028 or above to use SWFUpload."); 4 return false; 5 } 6 } 7 function loadFailed() { 8 alert("Something went wrong while loading SWFUpload. If this were a real application we'd clean up and then give you an alternative"); 9 } 10 11 function fileQueued(file) { 12 try { 13 var progress = new FileProgress(file, this.customSettings.progressTarget); 14 progress.setStatus("Pending..."); 15 progress.toggleCancel(true, this); 16 17 } catch (ex) { 18 this.debug(ex); 19 } 20 21 } 22 23 function fileQueueError(file, errorCode, message) { 24 try { 25 if (errorCode === SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED) { 26 alert("You have attempted to queue too many files.\n" + (message === 0 ? "You have reached the upload limit." : "You may select " + (message > 1 ? "up to " + message + " files." : "one file."))); 27 return; 28 } 29 30 var progress = new FileProgress(file, this.customSettings.progressTarget); 31 progress.setError(); 32 progress.toggleCancel(false); 33 34 switch (errorCode) { 35 case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT: 36 progress.setStatus("File is too big."); 37 this.debug("Error Code: File too big, File name: " + file.name + ", File size: " + file.size + ", Message: " + message); 38 break; 39 case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE: 40 progress.setStatus("Cannot upload Zero Byte files."); 41 this.debug("Error Code: Zero byte file, File name: " + file.name + ", File size: " + file.size + ", Message: " + message); 42 break; 43 case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE: 44 progress.setStatus("Invalid File Type."); 45 this.debug("Error Code: Invalid File Type, File name: " + file.name + ", File size: " + file.size + ", Message: " + message); 46 break; 47 default: 48 if (file !== null) { 49 progress.setStatus("Unhandled Error"); 50 } 51 this.debug("Error Code: " + errorCode + ", File name: " + file.name + ", File size: " + file.size + ", Message: " + message); 52 break; 53 } 54 } catch (ex) { 55 this.debug(ex); 56 } 57 } 58 59 function fileDialogComplete(numFilesSelected, numFilesQueued) { 60 try { 61 if (numFilesSelected > 0) { 62 document.getElementById(this.customSettings.cancelButtonId).disabled = false; 63 } 64 65 /* I want auto start the upload and I can do that here */ 66 this.startUpload(); 67 } catch (ex) { 68 this.debug(ex); 69 } 70 } 71 72 function uploadStart(file) { 73 try { 74 /* I don't want to do any file validation or anything, I'll just update the UI and 75 return true to indicate that the upload should start. 76 It's important to update the UI here because in Linux no uploadProgress events are called. The best 77 we can do is say we are uploading. 78 */ 79 var progress = new FileProgress(file, this.customSettings.progressTarget); 80 progress.setStatus("Uploading..."); 81 progress.toggleCancel(true, this); 82 } 83 catch (ex) {} 84 85 return true; 86 } 87 88 function uploadProgress(file, bytesLoaded, bytesTotal) { 89 try { 90 var percent = Math.ceil((bytesLoaded / bytesTotal) * 100); 91 92 var progress = new FileProgress(file, this.customSettings.progressTarget); 93 progress.setProgress(percent); 94 progress.setStatus("Uploading..."); 95 } catch (ex) { 96 this.debug(ex); 97 } 98 } 99 100 function uploadSuccess(file, serverData) { 101 try { 102 var progress = new FileProgress(file, this.customSettings.progressTarget); 103 progress.setComplete(); 104 progress.setStatus("Complete."); 105 progress.toggleCancel(false); 106 107 } catch (ex) { 108 this.debug(ex); 109 } 110 } 111 112 function uploadError(file, errorCode, message) { 113 try { 114 var progress = new FileProgress(file, this.customSettings.progressTarget); 115 progress.setError(); 116 progress.toggleCancel(false); 117 118 switch (errorCode) { 119 case SWFUpload.UPLOAD_ERROR.HTTP_ERROR: 120 progress.setStatus("Upload Error: " + message); 121 this.debug("Error Code: HTTP Error, File name: " + file.name + ", Message: " + message); 122 break; 123 case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED: 124 progress.setStatus("Upload Failed."); 125 this.debug("Error Code: Upload Failed, File name: " + file.name + ", File size: " + file.size + ", Message: " + message); 126 break; 127 case SWFUpload.UPLOAD_ERROR.IO_ERROR: 128 progress.setStatus("Server (IO) Error"); 129 this.debug("Error Code: IO Error, File name: " + file.name + ", Message: " + message); 130 break; 131 case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR: 132 progress.setStatus("Security Error"); 133 this.debug("Error Code: Security Error, File name: " + file.name + ", Message: " + message); 134 break; 135 case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED: 136 progress.setStatus("Upload limit exceeded."); 137 this.debug("Error Code: Upload Limit Exceeded, File name: " + file.name + ", File size: " + file.size + ", Message: " + message); 138 break; 139 case SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED: 140 progress.setStatus("Failed Validation. Upload skipped."); 141 this.debug("Error Code: File Validation Failed, File name: " + file.name + ", File size: " + file.size + ", Message: " + message); 142 break; 143 case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED: 144 // If there aren't any files left (they were all cancelled) disable the cancel button 145 if (this.getStats().files_queued === 0) { 146 document.getElementById(this.customSettings.cancelButtonId).disabled = true; 147 } 148 progress.setStatus("Cancelled"); 149 progress.setCancelled(); 150 break; 151 case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED: 152 progress.setStatus("Stopped"); 153 break; 154 default: 155 progress.setStatus("Unhandled Error: " + errorCode); 156 this.debug("Error Code: " + errorCode + ", File name: " + file.name + ", File size: " + file.size + ", Message: " + message); 157 break; 158 } 159 } catch (ex) { 160 this.debug(ex); 161 } 162 } 163 164 function uploadComplete(file) { 165 if (this.getStats().files_queued === 0) { 166 document.getElementById(this.customSettings.cancelButtonId).disabled = true; 167 } 168 } 169 170 // This event comes from the Queue Plugin 171 function queueComplete(numFilesUploaded) { 172 var status = document.getElementById("divStatus"); 173 status.innerHTML = numFilesUploaded + " file" + (numFilesUploaded === 1 ? "" : "s") + " uploaded."; 174 }
编写HTML页面:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>SWFUpload Demos - Simple Demo</title> 5 <link href="css/default.css" rel="stylesheet" type="text/css" /> 6 <script type="text/javascript" src="swfupload/swfupload.js"></script> 7 <script type="text/javascript" src="swfupload/swfupload.queue.js"></script> 8 <script type="text/javascript" src="js/fileprogress.js"></script> 9 <script type="text/javascript" src="js/handlers.js"></script> 10 11 <body> 12 <div id="header"> 13 <h1 id="logo"><a href="../">SWFUpload</a></h1> 14 <div id="version">v2.5.0</div> 15 </div> 16 17 <div id="content"> 18 <h2>External Interface Demo</h2> 19 <form id="form1" action="index.php" method="post" enctype="multipart/form-data"> 20 <p> This page tests rebuilding the External Interface after some kind of display change. This demo isn't meant for building upon. Rather it 21 helps test whether a particular browser is suffering from this bug.</p> 22 23 <div class="fieldset flash" id="fsUploadProgress"> 24 <span class="legend">Upload Queue</span> 25 </div> 26 <div id="divStatus">0 Files Uploaded</div> 27 <div id="divMovieContainer"> 28 <span id="spanButtonPlaceHolder"></span> 29 <input type="button" value="Start Upload" onclick="swfu.startUpload();" style="margin-left: 2px; font-size: 8pt; height: 29px;" /> 30 </div> 31 32 33 </form> 34 </div> 35 </body> 36 </html>
后台处里代码:
首先要配置SpringMVC配置文件,在配置文件中加入下面内容
1 <bean id="multipartResolver" 2 class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> 3 <!-- one of the properties available; the maximum file size in bytes --> 4 <property name="maxUploadSize" value="10485760000000" /> 5 </bean>
服务器接收程序:
1 @ResponseBody 2 @RequestMapping("/fileUpload.do") 3 public String fileUpload(HttpServletRequest request,@RequestParam("filedata")MultipartFile file) throws Exception { 4 5 request.setCharacterEncoding("utf-8");//解决乱码问题 6 try{ 7 String uploadDir = request.getServletContext().getRealPath("/upload");//获取上传目录的路径 8 //获得目录,如果目录不存在,则创建目录 9 File dirPath = new File(uploadDir); 10 if (!dirPath.exists()) { 11 dirPath.mkdirs(); 12 } 13 //开始文件上传 14 InputStream inputStream = file.getInputStream(); //获得输入流 15 String fileName = file.getOriginalFilename(); //获得原始名字 16 17 String fileNameFull = uploadDir + "/" + fileName; 18 OutputStream outputStream = new FileOutputStream(fileNameFull);//获得输出流 19 int bytesRead = 0; 20 byte[] buffer = new byte[8192]; 21 22 while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) 23 { 24 //输出到目标文件夹 25 outputStream.write(buffer, 0, bytesRead); 26 } 27 outputStream.close(); 28 // close the stream 29 inputStream.close(); 30 }catch(Exception e){ 31 e.printStackTrace(); 32 return "error"; 33 } 34 return "ok"; 35 }
这样经过上面的配置即可完成多文件同时上传的例子了,注意的地方有几点:
一是中文名乱码问题,解决办法:
1.在Java程序内加入一行代码:request.setCharacterEncoding("utf-8");
2.进行编码转换,fileName = new String(fileName.getBytes("GBK"),"UTF-8");//反正就是进行编码的转换
3.好像还有其它的方法,但是我也不太懂了
还有一点是后台获得file的时候名字问题:
后台获得file是一个一个获取的,即如果你上传10个文件,那就是分十次来调用后台Java代码的,每次调用上传一个文件,所以后台只需要对一个文件进行处理即可,这些文件传递到后台的name属性值是相同的,通过 file_post_name : "filedata"来进行配置,这个时候后台获取file只要通过filedata即可了!
这是我初步了解了SwfUpload之后进行记录的内容,希望以后自己能回来翻看学习。