理解和灵活应用 Struts2 的文件下载功能
文件下载给我们最直接的概念就是,给个文件链接点击就下载。似乎太简单,然而 Struts2 却把它作为一个独立的学问来对待,理由有四:
1. 文件名为中文时,直接点击下载,链接可能会走样(某些浏览器,URL 编码的问题),致使无法下载。
2. 不总是从下载实际的文件,文件内容有时候是动态生成的,如数据库中的内容。
3. 对于知名的文件类型不让浏览器直接打开,而是出现下载对话框保存文件。例如,要下载的文件是 .txt 的,可能直接就在浏览器中显示其内容。
4. 需要授权才能下载文件时
当然对于以上若干问题,Servlet/JSP 都能通过正确的 URL 编码,响应头设置、权限代码控制解决,只是 Struts2 让我们处理起来更方便了,内部原理自然是一样的。
先来看下 Servlet 如何实现文件下载的,直接见代码:
知道了上面各行的含义,再来看下 Struts2 的解决方式,其实不过是把某些代码的功能移入到了配置文件而已。在李刚所著的《Struts 2 权威指南》中说 Struts 实现文件下载是由一个 download 拦截器。其实不然,只是一个 StreamResult(org.apache.struts2.dispatcher.StreamResult) 而已,也不像实现文件上传那样要额外的 JAR 包。在 StreamResult 中有以下几个默认属性要留意一下:
public static final String DEFAULT_PARAM = "inputName";
protected String contentType = "text/plain";
protected String contentDisposition = "inline";
protected String inputName = "inputStream";
protected InputStream inputStream;
protected int bufferSize = 1024;
StreamResult 的实现细节敬请阅读它的源代码,实现过程一言以蔽之就是:从 inputStream 获取内容,以相应的 contentType、contentDisposition 和 bufferSize 输出给浏览器,对 contentType 和 contentDisposition 的相应设置就能实现文件下载,可对照前面 Servlet 的实现。看个实际的例子吧。
struts.xml 中 Action 的配置,假定 Action 类为 com.unmi.DownLoadAction
说明:对于上面的配置其他参数可以用默认值,关键就是 contentDisposition 要设置为 attachment 才能提示下载,同时用 filename 指定文件名,若直接指定非动态的文件名。
DownloadAction 代码,需要实现 getInputStream() 返回输入流;因前面用的动态文件名,所以须加上 getFileName() 返回文件名,若非动态文件名,则该方法可省去。
谨记一个就是,要想下载的文件名不乱码就要以 ISO8859-1 字符集进行转码,内容会否乱码可在调试中解决。
好啦,启动服务,访问 http://localhost:8080/teststruts2/download.action,浏览器便会提示下载 序列号(2009-06-17).txt,内容为:“Struts2 文件下载测试”。
参考:1. 浅谈Struts2下载文件的方法实现
2. struts2多文件动态下载及中文解决方案 永久链接 https://yanbin.blog/understand-apply-struts2-file-download/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
1. 文件名为中文时,直接点击下载,链接可能会走样(某些浏览器,URL 编码的问题),致使无法下载。
2. 不总是从下载实际的文件,文件内容有时候是动态生成的,如数据库中的内容。
3. 对于知名的文件类型不让浏览器直接打开,而是出现下载对话框保存文件。例如,要下载的文件是 .txt 的,可能直接就在浏览器中显示其内容。
4. 需要授权才能下载文件时
当然对于以上若干问题,Servlet/JSP 都能通过正确的 URL 编码,响应头设置、权限代码控制解决,只是 Struts2 让我们处理起来更方便了,内部原理自然是一样的。
先来看下 Servlet 如何实现文件下载的,直接见代码:
1PrintWriter out = response.getWriter();
2
3//不管实际类型,待下载文件 ContentType 统一指定为 application/octet-stream
4response.setContentType("application/octet-stream");
5
6//中文文件名必须转码为 ISO8859-1,否则为乱码
7String fileName = new String("文本文件.txt".getBytes(), "ISO8859-1");
8
9//作为附件下载,相应的 "inline;filename = "+fileName 是在线(浏览器中显示内容)打开
10response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
11
12//因为文件编码也为 ISO8859-1,所以内容须转码成 ISO8859-1,尚不知如何控制下载文本文件的编码
13//或有谁知道的,还请告诉我一下。 文件内容可以从物理文件中来,或者数据库中读取填入等等
14out.write(new String("Servlet 文件下载测试".getBytes(), "ISO8859-1"));
15
16out.close();知道了上面各行的含义,再来看下 Struts2 的解决方式,其实不过是把某些代码的功能移入到了配置文件而已。在李刚所著的《Struts 2 权威指南》中说 Struts 实现文件下载是由一个 download 拦截器。其实不然,只是一个 StreamResult(org.apache.struts2.dispatcher.StreamResult) 而已,也不像实现文件上传那样要额外的 JAR 包。在 StreamResult 中有以下几个默认属性要留意一下:
public static final String DEFAULT_PARAM = "inputName";
protected String contentType = "text/plain";
protected String contentDisposition = "inline";
protected String inputName = "inputStream";
protected InputStream inputStream;
protected int bufferSize = 1024;
StreamResult 的实现细节敬请阅读它的源代码,实现过程一言以蔽之就是:从 inputStream 获取内容,以相应的 contentType、contentDisposition 和 bufferSize 输出给浏览器,对 contentType 和 contentDisposition 的相应设置就能实现文件下载,可对照前面 Servlet 的实现。看个实际的例子吧。
struts.xml 中 Action 的配置,假定 Action 类为 com.unmi.DownLoadAction
1<action name="download" class="com.unmi.action.DownloadAction">
2 <result name="success" type="stream"><!--type 为 stream 应用 StreamResult 处理-->
3 <param name="contentType">application/octet-stream</param><!--默认为 text/plain-->
4
5 <!-- 默认就是 inputStream,它将会指示 StreamResult 通过 inputName 属性值的 getter 方法,
6 比如这里就是 getInputStream() 来获取下载文件的内容,意味着你的 Action 要有这个方法 -->
7 <param name="inputName">inputStream</param>
8
9 <!-- 默认为 inline(在线打开),设置为 attachment 将会告诉浏览器下载该文件,filename 指定下载文
10 件保有存时的文件名,若未指定将会是以浏览的页面名作为文件名,如以 download.action 作为文件名,
11 这里使用的是动态文件名,${fileName}, 它将通过 Action 的 getFileName() 获得文件名 -->
12 <param name="contentDisposition">attachment;filename="${fileName}"</param>
13 <param name="bufferSize">4096</param><!-- 输出时缓冲区的大小 -->
14 </result>
15</action>说明:对于上面的配置其他参数可以用默认值,关键就是 contentDisposition 要设置为 attachment 才能提示下载,同时用 filename 指定文件名,若直接指定非动态的文件名。
DownloadAction 代码,需要实现 getInputStream() 返回输入流;因前面用的动态文件名,所以须加上 getFileName() 返回文件名,若非动态文件名,则该方法可省去。
1package com.unmi.action;
2
3import java.io.*;
4import java.text.*;
5import java.util.Date;
6
7/**
8 * 文件下载的 Action
9 * @author Unmi
10 */
11public class NetbookSerialAction {
12
13 public String execute() throws Exception {
14 //这里可加入权限控制
15 return "success";
16 }
17
18 //获得下载文件的内容,可以直接读入一个物理文件或从数据库中获取内容
19 public InputStream getInputStream() throws Exception {
20 //return new FileInputStream("somefile.rar"); 直接下载 somefile.rar
21
22 //和 Servlet 中不一样,这里我们不需对输出的中文转码为 ISO8859-1
23 return new ByteArrayInputStream("Struts2 文件下载测试".getBytes());
24 }
25
26 //对于配置中的 ${fileName}, 获得下载保存时的文件名
27 public String getFileName() {
28 DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
29 String fileName = "序列号(" + df.format(new Date()) + ").txt";
30 try {
31 //中文文件名也是需要转码为 ISO8859-1,否则乱码
32 return new String(fileName.getBytes(), "ISO8859-1");
33 } catch (UnsupportedEncodingException e) {
34 return "impossible.txt";
35 }
36 }
37}谨记一个就是,要想下载的文件名不乱码就要以 ISO8859-1 字符集进行转码,内容会否乱码可在调试中解决。
好啦,启动服务,访问 http://localhost:8080/teststruts2/download.action,浏览器便会提示下载 序列号(2009-06-17).txt,内容为:“Struts2 文件下载测试”。
参考:1. 浅谈Struts2下载文件的方法实现
2. struts2多文件动态下载及中文解决方案 永久链接 https://yanbin.blog/understand-apply-struts2-file-download/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。