Struts2默认使用Jakarta的Common-FileUpload实现文件上传.
一、文件上传
1.简单上传
上传页面(uploadForm.jsp)
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>简单文件上传</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"> --> </head> <body> <s:form action="upload" enctype="multipart/form-data"> <!-- 注意这里设置enctype --> <s:textfield name="title" label="文件标题"/> <s:file name="upload" label="选择文件"/> <s:submit value="上传"/> </s:form> </body> </html>
实现上传的控制类(UploadAction.java)
package DeepUse; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.UUID; import com.opensymphony.xwork2.ActionSupport; public class UploadAction extends ActionSupport{ /** * */ private static final long serialVersionUID = 1L; private String title;//文件标题 private File upload;//上传文件域 private String uploadContentType;//封装上传文件类型 private String uploadFileName;//上传文件名属性 private String savePath;//保存路径 public String getTitle() {//以下是set和get方法 return title; } public void setTitle(String title) { this.title = title; } 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 getSavePath() { return savePath; } public void setSavePath(String savePath) { this.savePath = savePath; } @Override public String execute() throws Exception { // TODO Auto-generated method stub FileOutputStream fos=new FileOutputStream(getSavePath()+"/"+getUploadFileName()); FileInputStream fis=new FileInputStream(getUpload()); byte[] buffer=new byte[1024]; int len=0; while((len=fis.read(buffer))>0){//以二进制进行读写 fos.write(buffer,0,len); } return SUCCESS; } }
配置文件(struts.xml)
<action name="upload" class="DeepUse.UploadAction"> <param name="savePath">webapps/Structs2/uploadFiles</param> <result>/Struts2BaseUse/successforupload.jsp</result> </action>
上面savePath依据自己的路径进行配置,如果发现不怎么对,在UploadAction中使用绝对路径查看目录是否配置正确。参考
结果页面(successforupload.jsp)
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>上传成功</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"> --> </head> <body> 上传成功!<br/> 文件标题:<s:property value="title"/><br> 文件为:<img src="<s:property value="'uploadFiles/' + uploadFileName"/>"/><br/><!-- 注意如果文件名有中文,则找不到文件--> </body> </html>
为了避免文件名冲突,可以使用java.util.UUID类中的randomUUID方法来生成唯一的文件名,具体参考 UUID类的使用
我的uploadFiles文件夹是放在当前工程的目录下的
对于程序是如何取得文件类型以及文件名的? 这是采用约定的方式,类型File的xxx成员封装了文件域中的内容,类型为String的xxxFileName封装了对应的文件名,类型为String的xxxContentType封装了对应的文件类型.
(对于Action中的参数不仅可以通过页面参数来设置,还可以通过struts.xml配置文件的param来设置,同时还可以由地址栏访问action时带的对应get参数来设置)
2、自定义文件过滤
大部分时候,对于一个上传的文件系统需要实现过滤,不允许自由上传,这包括文件类型限制以及文件大小的限制.
修改上面的控制类(UploadAction.java)
package DeepUse; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import com.opensymphony.xwork2.ActionSupport; public class UploadAction extends ActionSupport{ /** * */ private static final long serialVersionUID = 1L; private String title;//文件标题 private File upload;//上传文件域 private String uploadContentType;//封装上传文件类型 private String uploadFileName;//上传文件名属性 private String savePath;//保存路径 private String allowTypes ;//允许的上传类型 public String getAllowTypes() { return allowTypes; } public void setAllowTypes(String allowTypes) { this.allowTypes = allowTypes; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } 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 getSavePath() { return savePath; } public void setSavePath(String savePath) { this.savePath = savePath; } @Override public String execute() throws Exception { // TODO Auto-generated method stub FileOutputStream fos=new FileOutputStream(getSavePath()+"/"+getUploadFileName()); FileInputStream fis=new FileInputStream(getUpload()); byte[] buffer=new byte[1024]; int len=0; while((len=fis.read(buffer))>0){ fos.write(buffer,0,len); } return SUCCESS; } public String filterTypes(String[] types) {//文件名任意 String fileType =getUploadContentType(); for(String type:types){ if(type.equals(fileType)){ return null; } } return ERROR; } @Override public void validate() { // TODO Auto-generated method stub String filterResult=filterTypes(getAllowTypes().split(","));//调用判断方法 if(filterResult!=null){ addFieldError("upload", "您要上传的文件类型不正确"); } } }
因为失败返回到input逻辑视图,因此修改struts.xml文件
<action name="upload" class="DeepUse.UploadAction"> <param name="savePath">webapps/Structs2/uploadFiles</param> <param name="allowTypes">image/png,image/gif,image/jpeg</param> <result>/Struts2BaseUse/successforupload.jsp</result> <result name="input">/Struts2DeepUse/uploadForm.jsp</result> </action>
为什么struts2对于文件上传失败会自动返回到input视图?这是因为在validate验证中,如果发现验证结果不符合预期,那么就会addFieldError方法调用,当struts2发现FieldError不为空就判断为程序出错,返回到input逻辑视图中.
3.拦截器实现过滤
将控制类还原成1中的控制类
修改配置文件(struts.xml)
<action name="upload" class="DeepUse.UploadAction"> <interceptor-ref name="fileUpload"> <param name="allowedTypes">image/png,image/gif,image/jpeg</param> <param name="maximumSize">2000</param><!-- 最大2KB --> </interceptor-ref> <interceptor-ref name="defaultStack"/> <!-- 由于过滤文件大小和文件内容必须要显示指明该引用,而且fileUpload必须在该引用的前面 --> <param name="savePath">webapps/Structs2/uploadFiles</param> <param name="allowTypes">image/png,image/gif,image/jpeg</param> <result>/Struts2BaseUse/successforupload.jsp</result> <result name="input">/Struts2DeepUse/uploadForm.jsp</result> </action>
下面是文件大小超出
下面是文件类型不符合
上面的错误信息使用的key分别是struts.messages.error.file.too.large和struts.messages.error.content.type.not.allowed,只要在自己资源文件中加以配置即可.还有一个key是struts.messages.error.uploading,这是排除上面两个的一个未知错误.(局部资源文件这里不行)
文件上传时候的stuts.properties文件常量有stuts.multipart.saveDir来设置自定义的临时文件目录,否则在work/catalina/localhost. 常量struts.multipart.maxSize来自定义文件大小.
上面的两个对于资源文件分别在struts2-core-2.2.1.jar下的org.apache.struts2两个default.properties和struts.messages.properties中
二、文件下载
1.一般下载
控制类(FileDownloadAction.java)
package DeepUse; import java.io.InputStream; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class FileDownloadAction extends ActionSupport{ /** * */ private static final long serialVersionUID = 1L; private String inputPath ; public void setInputPath(String inputPath) { this.inputPath = inputPath; } public InputStream getTargetFile() throws Exception{ InputStream in= ServletActionContext.getServletContext().getResourceAsStream(inputPath); return in;//返回输入流 } }
特别注意此处的getSevletContext()的路径,可以用.getRealPath(".")来查看,否则会找不到文件,报错没有输入流,错误信息参考 输入流Error
配置文件(struts.xml)
<action name="download" class="DeepUse.FileDownloadAction"> <param name="inputPath">/uploadFiles/listbox_1.png</param><!-- 结合servletContext的路径 --> <result type="stream"> <!--这个类型很重要,否则不定义会报找不到result --> <param name="inputName">targetFile</param> <!-- 对于action中的getTargetFile方法 --> <param name="contentDisposition">filename="myimage.png"</param> <!-- 描述名称任意 --> <param name="bufferSize">4096</param><!-- 缓冲大小 --> </result> </action>
这样在地址栏中输入.../download即可,因为这是action完成的下载,则网页左上角
2.授权下载
控制类(FileDownloadAction.java)
package DeepUse; import java.io.InputStream; import java.util.Map; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class FileDownloadAction extends ActionSupport{ /** * */ private static final long serialVersionUID = 1L; private String inputPath ; public void setInputPath(String inputPath) { this.inputPath = inputPath; } public InputStream getTargetFile() throws Exception{ InputStream in= ServletActionContext.getServletContext().getResourceAsStream(inputPath); return in; } @Override public String execute() throws Exception { // TODO Auto-generated method stub ActionContext ctx=ActionContext.getContext();//获取ActionContext实例 Map session=ctx.getSession(); //下面这些实现授权 String user=(String)session.get("user"); if(user!=null && user.equals("abc")){ return SUCCESS; } return LOGIN; } }
登录页面(loginForm.jsp)
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>登录界面</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"> --> </head> <body> <s:form action="loginForm"> <s:textfield name="user" label="用户名"/> <s:textfield name="pass" label="密码"/> <s:submit value="登录"/> </s:form> </body> </html>
下载页面(downloadForm.jsp)
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>单击链接下载文件</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"> --> </head> <body> 这是一个图片链接: <a href="download.action">下载</a> </body> </html>
配置文件(struts.xml)
<action name="download" class="DeepUse.FileDownloadAction"> <param name="inputPath">/uploadFiles/listbox_1.png</param> <result type="stream"> <param name="contentType">application/zip</param><!-- 指定文件的下载类型 --> <param name="inputName">targetFile</param> <param name="contentDisposition">filename="myimage.png"</param> <param name="bufferSize">4096</param><!-- 缓冲大小 --> </result> <result name="login">/Struts2DeepUse/loginForm.jsp</result> </action> <action name="loginForm" class="DeepUse.LoginPutSession"> <result>/Struts2DeepUse/downloadForm.jsp</result> </action>
登录控制类(LoginPutSession)
package DeepUse; import java.util.Map; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class LoginPutSession extends ActionSupport{ private String user; public String getUser() { return user; } public void setUser(String user) { this.user = user; } @Override public String execute() throws Exception { // TODO Auto-generated method stub ActionContext ctx=ActionContext.getContext(); Map session=ctx.getSession();//放入session 和上面的呼应 session.put("user", user); return SUCCESS; } }
流程是首先进入下载页面:
点击网页中下载,会进行验证,因为我们没有登录,因此验证失败,返回到登陆页面
当输入正确的用户名123之后,控制类LoginPutSession会将该用户名放入session中,并返回到之前下载的页面
此时进行再次下载,由于我们已经将用户abc放入session,因此这里会通过验证进行下载
下载成功,并且注意到这里的文件名正是在struts.xml中配置的filename.