一、要实现文件上传,需在项目中添加两个jar文件
二、上传准备的页面
注:必须植入enctype="multipart/form-data"属性,以及提交方式要设置成post
<h2>单个文件上传</h2> <s:form action="upload.action" enctype="multipart/form-data" method="post"> <s:textfield name="title" label="标题"/> <s:file name="upload" label="选择文件"/><br/> <s:submit name="submit" value="上传文件"></s:submit> </s:form>
实现文件上传的Action类
public class UploadAction extends ActionSupport{ //封装上传文件属性 private File upload; //封装上传文件的类型 private String uploadContentType; //封装上传文件名称 private String uploadFileName; //获取文件上传的路径 private String savePath; @Override public String execute() throws Exception { byte[] buffer=new byte[1024]; //读取文件 FileInputStream fis=new FileInputStream(getUpload()); //保存文件 FileOutputStream fos=new FileOutputStream(getSavePath()+"\\\\"+this.getUploadFileName()); int length=fis.read(buffer); while(length>0){ //每次写入length长度的内容 fos.write(buffer,0,length); length=fis.read(buffer); } fis.close(); fos.flush(); fos.close(); return SUCCESS; }
//获取文件上传的保存路径 通过读取存放目录获得保存路径
public String getSavePath() {
return ServletActionContext.getServletContext().getRealPath(savePath);
}
在Action中使用了三个属性封装文件信息:
File类型的XXX属性,与表单的File控件的name属性一样,用于封装File控件对应的文件内容
String类型的xxxFileName属性,该属性名称由前面的File类型属性和FileName组合,是固定的语法,是封装File控件对应文件的文件名
String类型的XXXContentType属性,同样由xxx属性和ContentType组合而成,是固定语法,封装File控件对应文件的文件类型
配置Action:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.devMode" value="true"></constant> <package name="default" namespace="/" extends="struts-default"> <!-- 文件上传 --> <action name="upload" class="cn.happy.action.UploadAction"> <!-- 通过param参数设置保存目录的路径 --> <param name="savePath">/upload</param> <result name="success">ok.jsp</result> </action> </package> </struts>
上传成功后的结果页面:植入的value是Action类中所对应的实体类属性
您所上传的文件是:<s:property value="uploadFileName"/><br/> 文件类型:<s:property value="uploadContentType"/>
实现效果:
三、多文件上传
与单文件上传的不同之处在于 将三个属性的类型修改成数组类型,并通过for循环进行遍历
public class SomeUploadAction extends ActionSupport { // 封装上传文件属性 private File[] upload; // 封装上传文件的类型 private String[] uploadContentType; // 封装上传文件名称 private String[] uploadFileName; // 封装文件上传的路径 private String savePath; public String execute() throws Exception { byte[] buffer = new byte[1024]; for (int i = 0; i < upload.length; i++) { FileInputStream fis = new FileInputStream(getUpload()[i]); FileOutputStream fos = new FileOutputStream(getSavePath() + "\\\\" + this.getUploadFileName()[i]); int length = fis.read(buffer); while (length > 0) { fos.write(buffer, 0, length); length = fis.read(buffer); } fos.flush(); fos.close(); fis.close(); } return SUCCESS; }
public String getSavePath() {
return ServletActionContext.getServletContext().getRealPath(savePath);
}
实现效果:
四、实现文件下载
为了支持文件的下载,Struts2框架提供了stream结果类型,该类型的作用就是专门用于实现文件下载的功能
实现文件下载:
由于在Struts2中实现文件下载时需要用到InputStream,所以在文件下载Action中要提供一个获得InputStream的方法,通过这个输入流将可以获取希望下载的文件内容
public class DownAction extends ActionSupport{ //读取下载文件的目录 private String inputPath; //下载文件的文件名 private String fileName; //读取下载文件的输入流 private InputStream inputStream; //下载文件的类型 private String conetntType; @Override public String execute() throws Exception { return SUCCESS; } //创建InputStream输入流 public InputStream getInputStream() throws FileNotFoundException { String path=ServletActionContext.getServletContext().getRealPath(inputPath); return new BufferedInputStream(new FileInputStream(path+"\\\\"+fileName)); }
通过Context上下文得到下载文件的实际路径,并构建了一个InputStream输入流实现文件下载读取
Action配置:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.devMode" value="true"></constant> <package name="default" namespace="/" extends="struts-default"> <!-- 文件下载 --> <action name="down" class="cn.happy.action.DownAction"> <param name="inputPath">/image</param> <result name="success" type="stream"> <param name="contentType">application/octet-stream</param> <param name="inputName">inputStream</param> <param name="contentDisposition">attachment;filename="${fileName}"</param> <param name="bufferSize">4096</param> </result> </action> </package> </struts>
在配置文件中,contentType参数决定了下载文件的类型。不同文件类型对应的参数值也是不同的。
通常情况下,contentType参数直接设置为application/octet-stream即可
contentDispoistion参数由两个部分组成,前面的部分表示处理文件的形式,如attachement表示在下载时弹出对话框,提示用户保存或者直接打开文件,而后一部分表示下载文件的名称,两部分之间以“;”进行分隔
当单击超链接时,即可下载
首先我们看看StreamResult这个类的源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class StreamResult extends StrutsResultSupport { private static final long serialVersionUID = -1468409635999059850L; protected static final Logger LOG = LoggerFactory.getLogger(StreamResult. class ); public static final String DEFAULT_PARAM = "inputName" ; protected String contentType = "text/plain" ; protected String contentLength; protected String contentDisposition = "inline" ; protected String contentCharSet ; protected String inputName = "inputStream" ; protected InputStream inputStream; protected int bufferSize = 1024 ; protected boolean allowCaching = true ; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
public class FileDownloadAction extends ActionSupport implements ServletRequestAware { private HttpServletRequest request; private String inputPath; private String fileName; private String target; private String mimeType; private String rootPath; private static Logger logger = Logger.getLogger(FileDownloadAction. class .getName()); /** * 注入输入流 * * @return * @throws Exception */ public InputStream getTargetFile() throws Exception { // 获取目标文件类型 mimeType = ServletActionContext.getServletContext().getMimeType( inputPath); String browserType = ServletActionContext.getRequest().getHeader( "User-Agent" ); // IE浏览器编码方式是URLEncoder编码 boolean isIE11 = browserType.contains( "rv" ) && browserType.contains( "Trident" ); if (browserType.contains( "MSIE" ) || isIE11) { fileName = URLEncoder.encode(fileName, "ISO8859-1" ); } return new FileInputStream(inputPath); } @Override public String execute() throws Exception { rootPath = request.getSession().getServletContext().getRealPath( "/" ); try { // 获取关键路径 String[] sep = inputPath.split( ":" ); String userId = (String) ActionContext.getContext() .getSession().get( "userId" ); if (userId == null ) { // 登陆控制 return "mainPage" ; } inputPath = rootPath + "//WEB-INF//uploadify-files//document//" + sep[ 0 ] + "//" + sep[ 1 ] + "//" + sep[ 2 ] + "//" + sep[ 3 ]; fileName = new String(sep[ 3 ].getBytes( "UTF-8" ), "ISO8859-1" ); return "success" ; } catch (Exception e) { e.printStackTrace(); return "errorPage" ; } } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<!-- 文件下载 --> < action name = "fileDownload" class = "com.bjhit.eranges.actions.download.FileDownloadAction" > <!-- 指定被下载资源的位置 --> < param name = "inputPath" >${inputPath}</ param > <!-- 配置结果类型为stream的结果 --> < result name = "success" type = "stream" > <!-- 指定下载文件的文件类型 --> < param name = "contentType" >${mimeType}</ param > <!-- 指定由getTargetFile()方法返回被下载文件的inputStream --> < param name = "inputName" >targetFile</ param > < param name = "contentDisposition" >attachment;filename="${fileName}"</ param > <!-- 指定下载文件的缓冲大小 --> < param name = "bufferSize" >1024</ param > </ result > </ action > |
1
|
< Connector URIEncoding = "UTF-8" connectionTimeout = "20000" port = "8080" protocol = "HTTP/1.1" redirectPort = "8443" useBodyEncodingForURI = "true" /> |
简单记录下,以便将来查阅。
文件的上传在此不多做解说,大家可以参考我写的这篇html5无插件的方式实现:http://my.oschina.net/gongxufan/blog/189824。不过该方法只适用于IE10+和ff chrome等高版本的浏览器。在这里主要讨论文件下载,下面开始进入主题。
首先我们看看StreamResult这个类的源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class StreamResult extends StrutsResultSupport { private static final long serialVersionUID = -1468409635999059850L; protected static final Logger LOG = LoggerFactory.getLogger(StreamResult. class ); public static final String DEFAULT_PARAM = "inputName" ; protected String contentType = "text/plain" ; protected String contentLength; protected String contentDisposition = "inline" ; protected String contentCharSet ; protected String inputName = "inputStream" ; protected InputStream inputStream; protected int bufferSize = 1024 ; protected boolean allowCaching = true ; } |
从该类的字段来看基本是围绕一个http响应头的设置,包括mineType,缓冲区大小,字符编码,下载的文件名等的设置。我们可以在Action中注入这些要用的字段值就可以了,当然需要在strut.xml中通过el动态配置。下面给出一个实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
public class FileDownloadAction extends ActionSupport implements ServletRequestAware { private HttpServletRequest request; private String inputPath; private String fileName; private String target; private String mimeType; private String rootPath; private static Logger logger = Logger.getLogger(FileDownloadAction. class .getName()); /** * 注入输入流 * * @return * @throws Exception */ public InputStream getTargetFile() throws Exception { // 获取目标文件类型 mimeType = ServletActionContext.getServletContext().getMimeType( inputPath); String browserType = ServletActionContext.getRequest().getHeader( "User-Agent" ); // IE浏览器编码方式是URLEncoder编码 boolean isIE11 = browserType.contains( "rv" ) && browserType.contains( "Trident" ); if (browserType.contains( "MSIE" ) || isIE11) { fileName = URLEncoder.encode(fileName, "ISO8859-1" ); } return new FileInputStream(inputPath); } @Override public String execute() throws Exception { rootPath = request.getSession().getServletContext().getRealPath( "/" ); try { // 获取关键路径 String[] sep = inputPath.split( ":" ); String userId = (String) ActionContext.getContext() .getSession().get( "userId" ); if (userId == null ) { // 登陆控制 return "mainPage" ; } inputPath = rootPath + "//WEB-INF//uploadify-files//document//" + sep[ 0 ] + "//" + sep[ 1 ] + "//" + sep[ 2 ] + "//" + sep[ 3 ]; fileName = new String(sep[ 3 ].getBytes( "UTF-8" ), "ISO8859-1" ); return "success" ; } catch (Exception e) { e.printStackTrace(); return "errorPage" ; } } } |
然后在xml中进行配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<!-- 文件下载 --> < action name = "fileDownload" class = "com.bjhit.eranges.actions.download.FileDownloadAction" > <!-- 指定被下载资源的位置 --> < param name = "inputPath" >${inputPath}</ param > <!-- 配置结果类型为stream的结果 --> < result name = "success" type = "stream" > <!-- 指定下载文件的文件类型 --> < param name = "contentType" >${mimeType}</ param > <!-- 指定由getTargetFile()方法返回被下载文件的inputStream --> < param name = "inputName" >targetFile</ param > < param name = "contentDisposition" >attachment;filename="${fileName}"</ param > <!-- 指定下载文件的缓冲大小 --> < param name = "bufferSize" >1024</ param > </ result > </ action > |
这里的注入方式是get,所以响应的字段要有get方法。在处理下载时中文文件名乱码的时候要主要IE的编码方式是URL编码的,要单独转换一下。然后get请求乱码的处理这里采用的是配置tomcat的URI编码来做的,否则需要自己进行编码解码。tomcat配置如下:
1
|
< Connector URIEncoding = "UTF-8" connectionTimeout = "20000" port = "8080" protocol = "HTTP/1.1" redirectPort = "8443" useBodyEncodingForURI = "true" /> |
总结:文件下载主要是考虑好存放路劲和访问控制,最好不要将敏感类型文件暴露在公众可以访问的目录,最好放web-inf或者本地文件系统。中文乱码主要是get请求乱码造成找不到文件,或者因为不同浏览器编码方式不一样导致中文文件名下载是乱码。解决办法是设置好tomcat服务器的URI编码方式为UTF-8,项目统一使用该编码方式,然后针对不同的浏览器采用不同的编码方式实现中文文件名的下载。
简单记录下,以便将来查阅。