用Iframe来实现无刷新文件上传的注意点javascript
1. 页面须要放一个iframe,并隐藏,而后让上传页面表单的target指定到这个iframecss
2. struts2里面,中文文件编码处理,能够在配置文件,也能够在action取上传文件名字的地方,看下面代码。html
3. 上传完成后,仍是提交返回到上传页面,可是后台上传成功的提示信息回传时,在js 里面,都必须用parent.xxx的方式,才能访问到iframe外面的变量和js方法,有点嵌套的意思,比较搞。java
4. 上传页面能够再放一个form,用来下载用。web
5. struts2通常出错或者取不到值,都是对valueStack和ognl 理解不许确致使的,#号访问valuestack中非root节点的值,不用#访问的是action里面属性的值,由于action默认是这个valuestack的root节点。ajax
6. struts2 action要取得属性值,前台form必须提交过去,就算没用到,就用隐藏域传,不然后台取回报空指针。多文件上传下载例子里有实例。spring
7. 无刷新上传,仍是不要用iframe,太麻烦。尤为是js访问iframe外面的方法和变量,或者提交表单,所有要加parent
apache
上代码json
上传action,BaseAction其实没啥,只是提炼了些公用方法,也是直接继承ActionSupport浏览器
package com.hello.web.upAndDownload; import java.io.File; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.hello.util.CommonConstants; import com.hello.util.FileUtil; import com.hello.web.BaseAction; import com.opensymphony.xwork2.ActionContext; /** * 单文件上传Action * */ public class FileUploadAction extends BaseAction { private static final long serialVersionUID = 3458871754829140760L; private Logger logger = LoggerFactory.getLogger(FileUploadAction.class); private File upload;//页面上传文件栏位名 private String uploadContentType;//文件类型 struts2框架使用,用于判断是否包含不支持的上传类型 private String uploadFileName;//文件名称 private String saveFileName;//文件转存后的名字 /**跳转到上传界面*/ public String goUploadPage(){ logger.debug("跳转到上传界面"); return "uploadPage"; } /** * 执行文件上传 * */ public String uploadFile(){ logger.debug("上传文件名称:"+uploadFileName); logger.debug("上传文件类型:"+uploadContentType); try { String fileName = FileUtil.generDateStrFilename(uploadFileName); HttpServletRequest request = ServletActionContext.getRequest(); String contextPath = request.getContextPath();//获取应用上下文路径 String destPath = ServletActionContext.getServletContext().getRealPath("/"+CommonConstants.uploadFilePath); //logger.debug("保存路径:"+destPath); File destFile = new File(destPath+File.separator+fileName); //拷贝文件 FileUtil.copyFile(upload, destFile); //将文件路径返回到页面,以供显示 //<s:property value="#request.saveFileName"/>方式访问 request.setAttribute("realFilePathName", contextPath+"/"+CommonConstants.uploadFilePath+"/"+fileName); //文件重名后的名字 this.setSaveFileName(fileName); }catch(Exception e){ logger.error(uploadFileName+"文件上传出错:",e); } return SUCCESS; } /** * 无刷新 * */ public String iframeUpload(){ logger.debug("上传文件名称:"+uploadFileName); logger.debug("上传文件类型:"+uploadContentType); try { String fileName = FileUtil.generDateStrFilename(uploadFileName); HttpServletRequest request = ServletActionContext.getRequest(); String contextPath = request.getContextPath();//获取应用上下文路径 String destPath = ServletActionContext.getServletContext().getRealPath("/"+CommonConstants.uploadFilePath); //logger.debug("保存路径:"+destPath); File destFile = new File(destPath+File.separator+fileName); //拷贝文件 FileUtil.copyFile(upload, destFile); //将文件路径返回到页面,以供显示 request.setAttribute("realFilePathName", contextPath+"/"+CommonConstants.uploadFilePath+"/"+fileName); //文件重名后的名字 this.setSaveFileName(fileName); ActionContext.getContext().put("message", "上传成功!"); }catch(Exception e){ logger.error(uploadFileName+"文件上传出错:",e); } return SUCCESS; } /**跳转到上传界面*/ public String goIframeUploadPage(){ logger.debug("跳转到上传界面"); return "iframeUpload"; } public File getUpload() { return upload; } public void setUpload(File upload) { this.upload = upload; } public String getUploadContentType() { return uploadContentType; } public void setUploadContentType(String uploadContentType) { this.uploadContentType = uploadContentType; } public String getUploadFileName() { return uploadFileName; } public void setUploadFileName(String uploadFileName) { this.uploadFileName = uploadFileName; } public String getSaveFileName() { return saveFileName; } public void setSaveFileName(String saveFileName) { this.saveFileName = saveFileName; } }
下载Action
package com.hello.web.upAndDownload; import java.io.InputStream; import java.net.URLEncoder; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.apache.struts2.ServletActionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.hello.util.CommonConstants; import com.hello.web.BaseAction; /** * 文件下载 * 必须用表单提交,且方法为POST,才能将隐藏域的值传进来 * jsp里面有fileName,则action里面使用setFileName方法对应,其余变量相似 * jsp里面取${downloadFileName},至关于调用action里面的getDownloadFileName方法 * * */ public class FileDownLoadAction extends BaseAction { private static final long serialVersionUID = -929993646660508444L; private Logger logger = LoggerFactory.getLogger(FileDownLoadAction.class); //下载显示的文件名 private String downloadFileName; private String realFileName; /** * 执行下载 * */ public String download(){ logger.debug("下载的文件显示名:"+downloadFileName); logger.debug("下载的文件实际名:"+realFileName); HttpServletRequest request = ServletActionContext.getRequest(); logger.debug("从隐藏域里面取得的值:"+request.getParameter("realFileName")); return SUCCESS; } public InputStream getInputStream() { logger.debug("下载的文件显示名:"+downloadFileName); logger.debug("下载的文件实际名:"+realFileName); //InputStream in = ServletActionContext.getServletContext().getResourceAsStream("/" +CommonConstants.uploadFilePath+"/"+ downloadFileName); InputStream in = ServletActionContext.getServletContext().getResourceAsStream("/" +CommonConstants.uploadFilePath+"/"+ realFileName); if(in==null){ logger.debug("检查action中文件下载路径是否正确!"); } return in; } public void setFileName(String fileName) { this.downloadFileName = fileName; } public String getDownloadFileName() { String finalFileName = downloadFileName; try { // 解决文件名称包含中文 HttpServletRequest request = ServletActionContext.getRequest(); String userAgent = request.getHeader("USER-AGENT"); if (StringUtils.contains(userAgent, "MSIE")) {// IE浏览器 finalFileName = URLEncoder.encode(downloadFileName, "UTF8"); } else if (StringUtils.contains(userAgent, "Mozilla")) {// google,火狐浏览器 finalFileName = new String(downloadFileName.getBytes(), "ISO8859-1"); } else { finalFileName = URLEncoder.encode(downloadFileName, "UTF8");// 其余浏览器 } } catch (Exception e) { logger.error("下载时,文件名称转码出错:", e); } return finalFileName; } public String getRealFileName() { logger.debug("调用 getRealFileName 方法:"+realFileName); return realFileName; } public void setRealFileName(String realFileName) { logger.debug("调用 setRealFileName 方法:"+realFileName); this.realFileName = realFileName; } }
jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>使用Iframe实现无刷新文件上传</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> <style type="text/css"> body { line-height: 1.6em; font-size: 12px; } .errorMessage ul { font-size: 12px; font-colr: red; backgroundColor:red; } </style> <script type="text/javascript"> var showName; var realName; function uploadfile(){ var ff = document.getElementsByName("upload"); if(ff[0].value==''|| ff[0].value==null){ alert('请至少选择文件!'); }else{ document.fileUpload.submit(); } } function download(){ parent.document.getElementById("fileName").value=parent.showName; //alert(parent.document.getElementById("fileName").value); parent.document.getElementById("realFileName").value=parent.realName; parent.document.downloadForm.action="<%= request.getContextPath()%>/file/fileDownload!download.htm"; parent.document.downloadForm.submit(); } function show(){ var msg = '<s:property value="#message"/>'; parent.showName = '<s:property value="uploadFileName"/>'; //var realFilePathName = '<s:property value="#request.realFilePathName"/>';//带路径的 parent.realName='<s:property value="saveFileName"/>';//不带路径的真实文件名 parent.document.getElementById("msg").innerHTML = msg; parent.document.getElementById("showFileName").innerHTML = "<a href=\"javascript:download();\">"+parent.showName+"</a>"; } </script> </head> <body onload="show();"> <s:form action ="iframeFileUpload" name="fileUpload" method ="POST" enctype ="multipart/form-data" namespace="/file" theme="simple" target="hidden_frame"> <s:actionerror/> <s:fielderror /> <!-- FF和IE浏览器显示的上传界面不同 --> 请选择文件:<s:file name ="upload"/><br/> (只支持上传JPG,PNG,GIF,TXT文件)<br/> <input type="button" name="btnUpload" value="上传" onclick="javascript:uploadfile();"/><br/> ---------------------------------------------------------------------<br/> <span id="msg"></span><br/> <span id="showFileName"></span><br/> <s:property value="#message"/> <s:if test="%{uploadFileName!=null}"> <s:if test="%{uploadFileName.lastIndexOf(\"txt\")>0}"></s:if> <s:else> <img src="<s:property value="#request.realFilePathName"/>"/><br/> </s:else> <a href="javascript:download();"><s:property value="uploadFileName"/></a><br/> </s:if> <iframe name='hidden_frame' id="hidden_frame" style="display:none;"></iframe> </s:form> <form action="<%= request.getContextPath()%>/file/fileDownload!download.htm" name="downloadForm" method="post"> <input type="hidden" name="fileName" id="fileName"/> <input type="hidden" name="realFileName" id="realFileName"/> </form> <a href="<s:url action="index" method="index" namespace="/"/>">返回首页</a><br> </body> </html>
配置文件
struts.xml主文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!-- struts2常量配置 --> <constant name="struts.objectFactory" value="spring" /> <constant name="struts.objectFactory.spring.useClassCache" value="true" /> <!-- 国际化配置 --> <constant name="struts.locale" value="zh_CN" /> <constant name="struts.i18n.encoding" value="UTF-8" /> <constant name="struts.custom.i18n.resources" value="resource.messages,resource.exceptions" /> <constant name="struts.action.extension" value="htm" /> <constant name="struts.ui.theme" value="simple" /> <!-- 部署时修改成false --> <constant name="struts.devMode" value="true" /> <!-- 该属性指定处理 MIME-type multipart/form-data文件上传 --> <constant name="struts.multipart.saveDir" value="/tmp"/> <!-- <constant name="struts.multipart.paeser" value="cos"/> --> <!-- 单位字节Byte 默认2M--> <constant name="struts.multipart.maxSize" value="2097152" /> <package name="my-default" extends="struts-default"> <result-types> <!-- 配置Json返回类型 --> <result-type name="json" class="org.apache.struts2.json.JSONResult"/> </result-types> <!-- 全局拦截器 --> <interceptors> <interceptor name="json" class="org.apache.struts2.json.JSONInterceptor"/> <interceptor name="globalErrInterceptor" class="com.hello.web.ErrorInterceptor"/> <interceptor-stack name="myDefaultStack"> <!-- 自定义错误拦截器栈 --> <interceptor-ref name="globalErrInterceptor"/> <!-- struts2 提供的拦截器栈,包含了struts2的不少核心拦截器 --> <interceptor-ref name="defaultStack" /> </interceptor-stack> </interceptors> <default-interceptor-ref name="myDefaultStack"/> <default-action-ref name="acctionNotFoundError"/> <global-results> <result name="jsonMsg" type="json"> <param name="root">message</param> </result> <result name="error">/WEB-INF/pages/error/404.jsp</result> <result name="500">/WEB-INF/pages/error/500.jsp</result> <result name="index">/index.jsp</result> <result name="testMessage">/WEB-INF/pages/msg/message.jsp</result> </global-results> <!-- 全局异常配置 --> <global-exception-mappings> <exception-mapping result="500" exception="java.lang.Exception"/> </global-exception-mappings> <action name="index" class="com.hello.web.IndexAction" method="index"> <result name="welcome">/index.jsp</result> </action> <!-- 使用 default-action-ref 配置struts2拦截到的404错误--> <action name="acctionNotFoundError" class="com.hello.web.PageNotFoundAction" method="go404Page"> <result name="pageNotFound">/WEB-INF/pages/error/404.jsp</result> </action> </package> <!-- struts2 修改配置文件路径后,必须加进来 有这里看出,当前文件的路径是跟路径下classes路径,因此其余的文件要从classes这个路径开始找 --> <include file="struts-plugin.xml" /> <include file="struts-default.xml" /> <!-- 本身添加的配置文件 注意路径 --> <include file="../config/struts-i18n-test.xml" /> <include file="../config/struts-validate-test.xml" /> <include file="../config/struts-tags-test.xml" /> <include file="../config/struts-upload2download-test.xml" /> </struts>
struts-upload2download-test.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="fileUpload2download" extends="my-default" namespace="/file"> <!-- 单文件上传 --> <action name="fileUpload" class="com.hello.web.upAndDownload.FileUploadAction" method="uploadFile"> <interceptor-ref name ="fileUpload"> <!-- 默认1M 若是上传文件在 maximumSize 和 struts.xml里面配置的 struts.multipart.maxSize 之间--> <!-- 会提示struts.messages.error.file.too.large这个错误 --> <!-- 大于struts.multipart.maxSize 提示 struts.messages.upload.error.SizeLimitExceededException 这个错误 --> <param name="maximumSize">1048576</param> <!-- 文件上传容许类型 --> <param name ="allowedTypes">image/bmp,image/png,image/gif,image/jpeg,image/jpg,text/plain</param> </interceptor-ref> <!-- 默认拦截器必须放在fileUpload以后 --> <interceptor-ref name ="defaultStack"/> <!-- 出错时跳转页面 --> <result name="input">/WEB-INF/pages/upload2down/upload.jsp</result> <!-- 上传成功显示页面 --> <result name="success">/WEB-INF/pages/upload2down/uploadSuccess.jsp</result> <result name="uploadPage">/WEB-INF/pages/upload2down/upload.jsp</result> </action> <!-- 文件下载 --> <action name="fileDownload" class="com.hello.web.upAndDownload.FileDownLoadAction" method="download"> <!-- 不指定result name 成功默认到success 出错到 input --> <result name="success" type="stream"> <!-- 下面这种方式也能避免乱码 --> <!-- <param name="contentType">application/octet-stream;charset=ISO8859-1</param> --> <param name="contentType">application/octet-stream</param> <param name="inputName">inputStream</param> <!-- 至关于fileDownload.getDownloadFileName --> <param name="contentDisposition">attachment;filename="${downloadFileName}"</param> <param name="bufferSize">4096</param> </result> </action> <!-- 多文件上传 --> <action name="multiFileUpload" class="com.hello.web.upAndDownload.MultiFileUploadAction" method="uploadFile"> <interceptor-ref name ="fileUpload"> <!-- 默认1M 若是上传文件在 maximumSize 和 struts.xml里面配置的 struts.multipart.maxSize 之间--> <!-- 会提示struts.messages.error.file.too.large这个错误 --> <!-- 大于struts.multipart.maxSize 提示 struts.messages.upload.error.SizeLimitExceededException 这个错误 --> <param name="maximumSize">1048576</param> <!-- 文件上传容许类型 --> <param name ="allowedTypes">image/bmp,image/png,image/gif,image/jpeg,image/jpg,text/plain</param> </interceptor-ref> <!-- 默认拦截器必须放在fileUpload以后 --> <interceptor-ref name ="defaultStack"/> <!-- 出错时跳转页面 --> <result name="input">/WEB-INF/pages/upload2down/multiUpload.jsp</result> <!-- 上传成功显示页面 --> <result name="success">/WEB-INF/pages/upload2down/multiUploadSuccess.jsp</result> <result name="multiUploadPage">/WEB-INF/pages/upload2down/multiUpload.jsp</result> </action> <action name="iframeFileUpload" class="com.hello.web.upAndDownload.FileUploadAction" method="iframeUpload"> <interceptor-ref name ="fileUpload"> <param name="maximumSize">1048576</param> <param name ="allowedTypes">image/bmp,image/png,image/gif,image/jpeg,image/jpg,text/plain</param> </interceptor-ref> <interceptor-ref name ="defaultStack"/> <result name="input">/WEB-INF/pages/upload2down/ajax1Upload.jsp</result> <!-- 上传成功显示页面 --> <result name="success">/WEB-INF/pages/upload2down/ajax1Upload.jsp</result> <result name="iframeUpload">/WEB-INF/pages/upload2down/ajax1Upload.jsp</result> </action> </package> </struts>