一、它能做什么?
借助于这个拦截器我们可以实现文件的上传和下载功能。
理论部分:
struts2的文件上传下载功能也要依赖于Apache commons-fileupload和Apache commons-io两个开源项目,要使用上传功能就需要把它们引入到classpath中。
比如考虑上传文件,几个关键点就是可以传什么类型的文件?可以传多大的文件?传过来以后放哪儿?叫什么名字?
当文件上传过来的时候是放在一个临时目录里的,我们在Action中持有一个成员属性File,这个File指向的就是服务器存放上传文件的临时目录中的实际文件(临时目录中的文件不会长久保存)
这个是default.properties中默认的一些属性,不配置临时目录的话默认就是用javax.servlet.context.tempdir
# uses javax.servlet.context.tempdir by default
struts.multipart.saveDir=
struts.multipart.maxSize=2097152
可以在配置文件中使用配置常量,这个配置的常量是全局的,意思就是struts.multipart.saveDir表示所有上传的文件都临时保存在这个目录中,而struts.multipart.maxSize指的是整个项目的上传文件的最大的size不能超过多少。
而下面这个是在action下设置的,针对的是单个的Action级别的:
allowedExtensionsSet 允许的文件类型扩展名,基于扩展名检查并不是特别靠谱
allowedTypesSet 允许的文件类型
maximumSize 允许的文件大小,这个zhide是一次上传的文件大小的上限
二、如何做?
1. 单文件上传:
首先来一个最简单的用法,即一次上传一个文件,参数都使用默认值。
只需要写一个前端:
<form action="uploadAction" method="post" enctype="multipart/form-data">
<input type="file" name="file"/><br/>
<input type="submit" value="上传" />
</form>
然后定义Action:
public class UploadAction extends ActionSupport { private File file; //与表单file控件的name一致,指向了服务器临时目录中的上传文件
private String fileContentType; //File变量的名+ContentType,表示文件的MIME类型
private String fileFileName; //File变量的名+FileName,表示文件上传之前的名称 @Override
public String execute() throws Exception { System.out.println(file+" "+fileContentType+" "+fileFileName); //do something... return SUCCESS;
} public File getFile() {
return file;
} public void setFile(File file) {
this.file = file;
} public String getFileContentType() {
return fileContentType;
} public void setFileContentType(String fileContentType) {
this.fileContentType = fileContentType;
} public String getFileFileName() {
return fileFileName;
} public void setFileFileName(String fileFileName) {
this.fileFileName = fileFileName;
} }
Action中的三个变量:
File file : 对于单文件上传类型,这个文件的名字必须是File类型的并且变量的名字要和前端表单的文件控件的name属性一致,会给这个变量赋值指向处于服务器上传文件临时目录中的那个文件。
fileContentType :文件的MIME类型,比如image/jpeg,application/octet-stream之类的。
fileFileName : 文件名,文件在上传之前在客户端的磁盘上存在就必然有一个名字,这里就是那个名字。
总结一下单文件上传:
1.表单的enctype="multipart/form-data"属性。
2.Action的三个成员变量file(文件本身),fileContentType(文件MIME类型),fileFileName(文件名)。
2. 多文件上传
<form action="uploadAction" method="post" enctype="multipart/form-data">
<input type="file" name="file"/><br/>
<input type="file" name="file"/><br/>
<input type="submit" value="上传" />
</form>
Action:
public class UploadAction extends ActionSupport { private File file[]; // 与表单file控件的name一致,指向了服务器临时目录中的上传文件
private String fileContentType[]; // File变量的名+ContentType,表示文件的MIME类型
private String fileFileName[]; // File变量的名+FileName,表示文件上传之前的名称 @Override
public String execute() throws Exception { for(int i=0;i<file.length;i++){
System.out.println(file[i]+" "+fileContentType[i]+" "+fileFileName[i]);
} // do something... return SUCCESS;
} public File[] getFile() {
return file;
} public void setFile(File[] file) {
this.file = file;
} public String[] getFileContentType() {
return fileContentType;
} public void setFileContentType(String[] fileContentType) {
this.fileContentType = fileContentType;
} public String[] getFileFileName() {
return fileFileName;
} public void setFileFileName(String[] fileFileName) {
this.fileFileName = fileFileName;
} }
或者:
public class UploadAction extends ActionSupport { private List<File> file; // 与表单file控件的name一致,指向了服务器临时目录中的上传文件
private List<String> fileContentType; // File变量的名+ContentType,表示文件的MIME类型
private List<String> fileFileName; // File变量的名+FileName,表示文件上传之前的名称 @Override
public String execute() throws Exception { for (int i = 0; i < file.size(); i++) {
System.out.println(file.get(i) + " " + fileContentType.get(i) + " "
+ fileFileName.get(i));
} // do something... return SUCCESS;
} public List<File> getFile() {
return file;
} public void setFile(List<File> file) {
this.file = file;
} public List<String> getFileContentType() {
return fileContentType;
} public void setFileContentType(List<String> fileContentType) {
this.fileContentType = fileContentType;
} public List<String> getFileFileName() {
return fileFileName;
} public void setFileFileName(List<String> fileFileName) {
this.fileFileName = fileFileName;
} }
多文件上传的要点就是在单文件上传的基础上前端多个名字相同的表单文件域,Action接收的地方改成File[]数组或者List<File>集合都可以。
3. 文件下载
配置文件:
<?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="default" namespace="/" extends="struts-default" >
<action name="downloadAction" class="struts_practice_001.DownloadAction">
<!-- 要将结果类型设置为stream -->
<result type="stream">
<!-- inputName跟Action中的输入流的名字要相同 -->
<param name="inputName">file</param>
<!-- contentDisposition的attachment表示让浏览器的默认行为是保存文件,而filename是保存文件的时候的默认名字 -->
<param name="contentDisposition">attachment;filename="psd.jpg"</param>
</result>
</action>
</package>
</struts>
Action:
public class DownloadAction extends ActionSupport { private InputStream file; //将要下载的文件读到这个输入流中 @Override
public String execute() throws Exception {
file=ServletActionContext.getServletContext().getResourceAsStream("psb.jpg");
return SUCCESS;
} public InputStream getFile() {
return file;
} public void setFile(InputStream file) {
this.file = file;
} }
文件下载的关键点:
1. Action中有一个InputStream,里面是要被下载的文件的流。
2.配置文件中配置一个为stream类型的结果,传入两个参数:inputName跟Action的InputStream名字保持一致,contentDisposition是给浏览器看的提示。
4.配置参数详解
<struts>
<!-- 文件上传到服务器的临时目录 -->
<constant name="struts.multipart.saveDir" value="/tempUpload"></constant>
<!-- 允许的文件最大尺寸(全局) -->
<constant name="struts.multipart.maxSize" value="1024000000"></constant> <package name="default" namespace="/" extends="struts-default" >
<action name="uploadAction" class="struts_practice_001.UploadAction">
<result>/success.jsp</result>
<interceptor-ref name="fileUpload">
<!-- 允许的MIME类型 -->
<param name="allowedTypes">image/bmp,image/png,image/gif,image/jpeg</param>
<!-- 允许上传文件的最大尺寸 -->
<param name="maximumSize">1024000</param>
</interceptor-ref>
<!-- 一定要写在后面 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
</package>
</struts>
这里有一点关于参数上的疑问,就是<constant name="struts.multipart.saveDir" value="/tempUpload" />我这样写的话这个目录究竟保存到了哪里呢?看别的博客上说的是
发现我的也是保存到了E盘上,但是这种说法很没有是根本没有说服力,设想一下如果让我们来设计的话,肯定要优先保存到剩余可用空间最大的分区上啦。
如果路径没有以/开头的话就是保存到user.dir。
参考资料:Struts2中的struts.multipart.saveDir的配置
三、原理是啥?
。。。。好长,以后闲下来再慢慢分析源码吧。。。
(没错,就这么太监了.....我发现写东西是件很奇妙的事情,有的时候连着写好几个小时感觉很爽,有的时候写不到十分钟就感觉头和蛋一起疼明明写不出来还硬要使劲挤牙膏....o(╯□╰)o)