SWFUpload是一个客户端文件上传工具,最初由Vinterwebb.se开发,它通过整合Flash与JavaScript技术为WEB开发者提供了一个具有丰富功能继而超越传统<input type="file" />标签的文件上传模式。
当前版本v2.2.0.1
官网示例:demo.swfupload.org
官方文档:http://demo.swfupload.org/Documentation/
下载地址:https://code.google.com/p/swfupload/downloads/list
示例环境:
Jboss323 + Struts 1
主要特点
- 可以同时选择多个文件
- 类似AJAX的无刷新上传
- 可以显示上传进度
- 良好的浏览器兼容性
- 兼容其他JavaScript库 (例如:jQuery, Prototype等)
- 支持Flash 8和Flash 9及更高版本
原理简介:
传统File标签多文件上传
SWFUpload多文件上传
开始配置
准备工作:
从官网下载的压缩包中,拿到以下文件(附件中有整理好的文件)
SWFUpload v2.2.0.1 Core\swfupload.js
SWFUpload v2.2.0.1 Core\Flash\swfupload.swf
SWFUpload v2.2.0.1 Samples\demos\simpledemo\js\fileprogress.js
SWFUpload v2.2.0.1 Samples\demos\simpledemo\js\handlers.js
SWFUpload v2.2.0.1 Samples\demos\simpledemo\js\swfupload.queue.js
放在指定文件夹中
deploy\media\swfupload
swfupload.swf、swfupload.js是核心文件
handlers.js是事件处理
fileprogress.js是文件队列处理
mimetype.properties是mime类型码表
从页面初始化到用户选择文件最后上传结束是这样的一个过程:
a页面载入(初始化选择控件)—b用户选择多文件—c形成文件队列—d触发上传(队列中的文件依次上传)—e后台处理—f向前台返回结果
其中d-e-f将根据文件数量重复执行,从a-f过程中所有的动作都由事件来驱动,可以自定义捕获每个事件,所以对于程序员来说非常适合订制自己的上传界面。
SWFUpload官方对PHP支持比较好,JSP需要修改和移植,而为了适配Struts 1.x更需要解决如下问题:
1、对表单验证失败的不进行上传,避免服务器资源浪费。
解决办法:用户批量选择完文件后,先存入队列,点击页面的提交按钮,先验证表单各项都正确无误,然后上传文件,最后进行提交。
2、SWFUpload上传的文件使用file.getContentType()取得的MIME类型总是多媒体。
解决办法:使用MimetypesFileTypeMap用后缀名从资源文件中获取MIME类型。
ManageAffixService.createAttachmentUseSWF(String aWorkID, String aSource)
3、SWFUpload使用的编码格式是UTF-8,中文名称在GBK下会成乱码。
解决办法:把上传的文件名作为参数,后台用URLDecoder转码获取正确的文件名。
JS控制
/** * handler.js */ function uploadStart(file) { try { /* 设置参数 aFileName 文件名称,字符集转码(防止中文乱码) flag 标识,上传 */ var post_params = {"aFileName":encodeURIComponent(file.name),"flag_do":"upload"}; this.setPostParams(post_params); var progress = new FileProgress(file, this.customSettings.progressTarget); progress.setStatus("Uploading..."); progress.toggleCancel(true, this); } catch (ex) {} return true; } function fileQueueError(file, errorCode, message) { try { if (errorCode === SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED) { alert("您选择的文件超过文件数量限制。\n" + (message === 0 ? "您已达到上传限制。" : (message > 1 ? "您可以再添加 " + message + " 个文件。" : "您不能再添加文件。"))); return; } var errInfo = "添加失败\n原因:"; /* comment by stephen var progress = new FileProgress(file, this.customSettings.progressTarget); progress.setError(); progress.toggleCancel(false); */ switch (errorCode) { case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT: //progress.setStatus("File is too big."); errInfo = errInfo + "该文件超过" + this.settings.file_size_limit + "的大小限制。"; this.debug("Error Code: File too big, File name: " + file.name + ", File size: " + file.size + ", Message: " + message); break; case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE: //progress.setStatus("Cannot upload Zero Byte files."); errInfo = errInfo + "0字节文件"; this.debug("Error Code: Zero byte file, File name: " + file.name + ", File size: " + file.size + ", Message: " + message); break; case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE: //progress.setStatus("Invalid File Type."); errInfo = errInfo + "文件类型错误,系统允许的文件类型为:" + this.settings.file_types; this.debug("Error Code: Invalid File Type, File name: " + file.name + ", File size: " + file.size + ", Message: " + message); break; default: if (file !== null) { //progress.setStatus("Unhandled Error"); errInfo = errInfo + "系统未知错误"; } this.debug("Error Code: " + errorCode + ", File name: " + file.name + ", File size: " + file.size + ", Message: " + message); break; } alert(errInfo + "\n文件:" + file.name + "(" + getNiceFileSize(file.size) +")"); } catch (ex) { this.debug(ex); } } function uploadSuccess(file, serverData) { var isSuccess = (serverData.indexOf("successed")==0?true:false); try { if(isSuccess){ var tr = document.getElementById(file.id); tr.style.color="green"; var affixID = serverData.substring(serverData.indexOf("affixListString=")+"affixListString=".length); //每成功上传一个文件,将附件ID返回至页面,保存在Hidden中,多个以,拼接 document.getElementsByName("affixListString")[0].value = affixID + "," + document.getElementsByName("affixListString")[0].value; }else{ var tr = document.getElementById(file.id); tr.style.color="red"; } } catch (ex) { this.debug(ex); } } function uploadComplete(file) { //上传完成后,判断队列中文件数是否为零 if (this.getStats().files_queued === 0) { //如果为零,说明文件已经全部上传,此时提交表单 document.forms[0].submit(); } }
/* * fileprogress.js */ function FileProgress(file,fileListID,swfUploadInstance) { if(!document.getElementById(file.id)){ /* 选择文件后新增一行, * 内容包括:文件图标 + 文件名称 + 文件大小 + 删除按钮 */ var tb = document.getElementById(fileListID); var tr = tb.insertRow(); tr.setAttribute("id",file.id); var td = tr.insertCell(); var fileName = file.name; var fileSize = "0B"; if(file.size){ fileSize=getNiceFileSize(file.size); } var fileICON = "<img src='/media/js/swfupload/attachment.png' border='0'/>"; var fileDelButton = "<span id="+file.id+"_del><a><img src='/media/default/images/del.gif' alt='删除' border='0'/></a></span>"; td.innerHTML= fileICON + fileName + "<font color='gray'>(" + fileSize + ")</font>" + fileDelButton; var delObject = document.getElementById(file.id+"_del"); delObject.onclick = function () { swfUploadInstance.cancelUpload(file.id); var tb = document.getElementById(fileListID); var tr = document.getElementById(file.id); tb.deleteRow(tr.rowIndex); }; } }
JSP页面
- 引入JS文件
- 定义初始化参数
- 声明、创建SWF对象
- 修改提交方式
<%-- 引入JS文件 --%> <script type="text/javascript" src="/media/js/swfupload/swfupload.js"></script> <script type="text/javascript" src="/media/js/swfupload/swfupload.queue.js"></script> <script type="text/javascript" src="/media/js/swfupload/fileprogress.js"></script> <script type="text/javascript" src="/media/js/swfupload/handlers.js" charset="GBK"></script> <script type="text/javascript" src="/media/js/swfupload/swfupload.cookies.js"></script> <script type="text/javascript" src="/media/js/swfupload/swfupload.swfobject.js"></script> <%-- 定义参数 --%> <bean:define id="courseLecturesForm" type="gds.jwext.lecture.prez.CourseLecturesForm" name="courseLecturesForm" scope="request" toScope="page"/> <% //单个文件允许的max大小 double perMaxSize = Double.parseDouble(courseLecturesForm.getTheFileSize()+""); //perMaxSize数据对应的单位 String sizeUnit = "KB"; //允许上传的文件类型 String ext = "*.*"; //文件上传提交的目标页面 StringBuffer uploadUrl = new StringBuffer("http://"); uploadUrl.append(request.getHeader("Host")); uploadUrl.append(request.getContextPath()); uploadUrl.append("/jwext/modifyLectureAction.do"); %> <%-- 定义SWFupload对象 --%> <script type="text/javascript"> <!-- var swfu; var swfStats; var fileNumbers = <%=((List)courseLecture.getAffixAccessoryList()).size() %>; SWFUpload.onload = function () { var settings = { flash_url : "/media/js/swfupload/swfupload.swf", upload_url: "<%=uploadUrl.toString()%>", file_size_limit : "<%=perMaxSize%> <%=sizeUnit%>", file_types : "<%=ext%>", file_types_description : "<%=ext%>", file_upload_limit : 5, file_queue_limit : 5, file_post_name: "uploadFileData", custom_settings : { myFileListTarget : "idFileList" }, debug: false, auto_upload: false, // button_text: '<span class="theFont">Hello</span>', // button_text_style: ".theFont { font-size: 16; }", // button_text_left_padding: 12, // button_text_top_padding: 3, button_image_url: "/media/js/swfupload/SmallSpyGlassWithTransperancy_67x18.png", button_width: "67", button_height: "18", button_align: "absmiddle", button_placeholder_id: "spanButtonPlaceholder", button_cursor : SWFUpload.CURSOR.HAND, button_window_mode : SWFUpload.WINDOW_MODE.WINDOW, button_action : SWFUpload.BUTTON_ACTION.SELECT_FILES, // The event handler functions are defined in handlers.js swfupload_loaded_handler : swfUploadLoaded, file_queued_handler : fileQueued, file_queue_error_handler : fileQueueError, file_dialog_complete_handler : fileDialogComplete, upload_start_handler : uploadStart, upload_progress_handler : uploadProgress, upload_error_handler : uploadError, upload_success_handler : uploadSuccess, upload_complete_handler : uploadComplete, queue_complete_handler : queueComplete, // Queue plugin event // SWFObject settings minimum_flash_version : "9.0.28", swfupload_pre_load_handler : swfUploadPreLoad, swfupload_load_failed_handler : swfUploadLoadFailed }; swfu = new SWFUpload(settings); }; //--> </script> <%-- 上传并提交 --%> <SCRIPT LANGUAGE="JavaScript"> //提交时验证表单 function submitCheck(){ try{ with(document.forms[0]){ var result = CheckNull(courseSubject, employeeName, lectureDate, hourArmy, miniteArmy, classroomName); if(!result){ return false; } if (swfu.getStats().files_queued > 0) { swfu.startUpload(); }else{ document.forms[0].submit(); } } return true; }catch(e){ debug(e.message); return false; } } //删除已上传文件,修改页面需要 function delAffixFun(affixID){ with(document.forms[0]){ delAffixArr.value = delAffixArr.value + affixID + ","; document.getElementById(affixID).style.display="none"; swfStats.successful_uploads--; swfu.setStats(swfStats); } } </SCRIPT> <%-- 页面元素 --%> <div id="main"> <div id="list"> <table cellpadding="0" cellspacing="0" width="98%" border="0" align="center" class="tabular"> <tr> <th>附件:</th> <td colspan="3" valign="middle" > <input type="hidden" name="affixListString"/> <span id="spanButtonPlaceholder"></span> <table id="idFileList" width="400" align="left" cellpadding="0" cellspacing="0" border="0" style="width:400px;overflow:hidden;border-style: dotted dashed solid double; "></table> </td> </tr> </table> <div id="freeFormButton"> <button type="button" name="addButton" onclick="submitCheck()"><div><b>保存</b></div></button> <button type="button" onclick="window.close()"><div><b>取消</b></div></button> </div>
Struts Form
- 声明接收FormFile的对象
- 声明获得附件最大限制的getter方法
/** 附件大小 */ private int theFileSize; /** 附件 */ private FormFile uploadFileData; public void setTheFileSize(int theFileSize) { this.theFileSize = theFileSize; } public int getTheFileSize() { ManageSysConfigService manageSysConfigService = ManageSysConfigService. getInstance(); try { String strSize = manageSysConfigService.findParamValueByParamName( SystemConstant. MESSAGE_ATTACHMENT_SIZE); if (strSize != null && !"".equals(strSize)) theFileSize = Integer.parseInt(strSize); } catch (Exception ex) { theFileSize = 500; } return theFileSize; } public FormFile getUploadFileData() { return uploadFileData; } public void setUploadFileData(FormFile uploadFileData) { this.uploadFileData = uploadFileData; }
Struts Action
- 从参数来获得上传的标识,进行上传的动作
- 从参数中获得上传文件名,进行转码
- 调用manageAffixService.createAttachmentUseSWF方法,解决MIME类型的问题
if ("upload".equals(flag_do)) { //上传附件,得到新增附件ID String aAccessoryID = addAffix(courseLecturesForm, request); response.setContentType("text/html; charset=GBK"); response.getWriter().print("successed;affixListString=" + aAccessoryID); response.flushBuffer(); return null; } //增加附件 String affixID = request.getParameter("affixListString"); /** 上传附件获取附件ID * @param request * @param classForm * @return 附件ID * @throws java.lang.Exception */ private String addAffix(CourseLecturesForm courseLecturesForm, HttpServletRequest request) throws Exception { try { FormFile formFile = courseLecturesForm.getUploadFileData(); //获取文件名称(避免中文乱码) String fName = URLDecoder.decode(request.getParameter("aFileName"), "UTF-8"); //读取上传人主机IP String clientHostIP = request.getRemoteHost(); ManageAffixService manageAffixService = ManageAffixService. getInstance(); //上传附件并得到附件ID String affixID = manageAffixService.createAttachmentUseSWF( formFile, fName, "讲座课附件", clientHostIP); return affixID; } catch (Exception ex) { ex.printStackTrace(); throw ex; } }
Struts Service
- 负责上传附件
/** * 生成附件,且将附件存储在附件表中 * @param file struts的formFile * @param fileName 转码后的文件名(避免中文乱码) * @param attachmentDesc 附件的简介 * @param clientIP 上传的ip地址 * @return String 附件ID * @throws AppException */ public String createAttachmentUseSWF(FormFile file, String fileName, String attachmentDesc, String clientIP) throws Exception { try { if (file == null || file.getFileSize() == 0) { throw new AppException(String.valueOf( "对不起,您上传的文件可能没有内容,请确认您上传的文件是否正确。(文件的大小要大于0个字节。)")); } if (fileName.getBytes().length > 60) { throw new AppException(String.valueOf( "对不起,您上传的文件标题过长,请修改文件标题后重新上传。(文件标题不能超过60个字节。(包括扩展名,一个汉字以2个字节计算))")); } String suffix = ""; /** * 取附件的后缀名 */ if (fileName.indexOf(".") > -1) { String[] names = fileName.split("\\."); suffix = names[names.length - 1]; if (suffix.equalsIgnoreCase("exe") || suffix.equalsIgnoreCase("bat") || suffix.equalsIgnoreCase("cmd") || suffix.equalsIgnoreCase("com") || suffix.equalsIgnoreCase("sys") || suffix.equalsIgnoreCase("bin") || suffix.equalsIgnoreCase("dll")) { throw new AppException("ERROR_AFFIX_FILETYPE_EXECUTE"); } if (suffix.equalsIgnoreCase("asp") || suffix.equalsIgnoreCase("jsp") || suffix.equalsIgnoreCase("js") || suffix.equalsIgnoreCase("php") || suffix.equalsIgnoreCase("pl") || suffix.equalsIgnoreCase("aspx") || suffix.equalsIgnoreCase("css") || suffix.equalsIgnoreCase("htc")) { throw new AppException("ERROR_AFFIX_FILETYPE_EXESCRIPT"); } } int fileSize = file.getFileSize(); String fileID = ""; if (!suffix.equals("")) { //如果后缀名不为空 fileID = gds.util.UUIDGenerator.create("gds") + "." + suffix; } else { //为空 fileID = gds.util.UUIDGenerator.create("gds"); } AccessoryDTO accessoryDTO = new AccessoryDTO(); //获取SWF上传文件的MIME Type ,在mimetype.properties文件中维护 String pFilename = "/mimetype.properties"; // 构翠文件名 InputStream in = null; Properties p = new Properties(); try { in = getClass().getResourceAsStream(pFilename); p.load(in); // 读入属使 } catch (Exception e) { e.printStackTrace(); log.fatal("在系统的classpath下找不到mimetype.properties文件!" + e); } finally { try { if (in != null) { in.close(); } } catch (Exception ex) { log.error(ex); } } String mimeType = ""; try { mimeType = p.getProperty(suffix); } catch (MissingResourceException e) { mimeType = (new MimetypesFileTypeMap()).getContentType(fileName); // e.printStackTrace(); } ManageAffixService service = ManageAffixService.getInstance(); accessoryDTO.setAccessoryID(fileID); accessoryDTO.setClientHostIP(clientIP); accessoryDTO.setAccessoryName(fileName); accessoryDTO.setFileSize(fileSize / 1024); //设置附件的大小,k为单位 accessoryDTO.setFileExt(suffix); //附件的扩展名 if (justMimeTypeExist(mimeType)) { //判断附件后缀名是否存在 accessoryDTO.setMimeType(mimeType); } else { accessoryDTO.setMimeType("其它"); } accessoryDTO.setAccessoryDesc(attachmentDesc); java.util.Date tmp = new Date(); accessoryDTO.setUploadTime(tmp); SimpleDateFormat adf = new SimpleDateFormat("yyyy-MM"); String upTime = adf.format(tmp); String upRoute = (gds.jap.common.AppSettingFactory.getInstance()). getAppSetting("attachment.uploadDirectory"); File dirFile = new File(upRoute); if (!dirFile.exists()) { dirFile.mkdirs(); } dirFile = new File(upRoute + upTime); if (!dirFile.exists()) { dirFile.mkdir(); } java.io.OutputStream bos = null; bos = new java.io.FileOutputStream( upRoute + upTime + File.separator + fileID); byte[] buffer = new byte[fileSize]; file.getInputStream().read(buffer, 0, fileSize); bos.write(buffer); service.addAccessory(accessoryDTO); //在数据库中增加附件 bos.close(); return fileID; } catch (Exception ex) { ex.printStackTrace(); throw ex; } } }