用Iframe来实现无刷新文件上传的注意点
1. 页面需要放一个iframe,并隐藏,然后让上传页面表单的target指定到这个iframe
2. struts2里面,中文文件编码处理,可以在配置文件,也可以在action取上传文件名字的地方,看下面代码。
3. 上传完成后,还是提交返回到上传页面,但是后台上传成功的提示信息回传时,在js 里面,都必须用parent.xxx的方式,才能访问到iframe外面的变量和js方法,有点嵌套的意思,比较搞。
4. 上传页面可以再放一个form,用来下载用。
5. struts2一般出错或者取不到值,都是对valueStack和ognl 理解不准确导致的,#号访问valuestack中非root节点的值,不用#访问的是action里面属性的值,因为action默认是这个valuestack的root节点。
6. struts2 action要取得属性值,前台form必须提交过去,就算没用到,就用隐藏域传,否则后台取回报空指针。多文件上传下载例子里有实例。
7. 无刷新上传,还是不要用iframe,太麻烦。尤其是js访问iframe外面的方法和变量,或者提交表单,全部要加parent
上代码
上传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>