1. 文件下载的路径问题,当项目运行时MyEclipse会把项目加载到Tomcat的webapps目录下面。所以,关于路径问题(一定要设置相对路劲)
2. 相对服务器而言,下载到本地要用相对路径。上传到客户端要用绝对路径好一点
3. 上传文件所需要的包一定不能少
1. Struts 2 对文件下载的支持
Struts 2 通过 org.apache.slruts2.dispatcher.StreamResuIt 结果类型来支持文件下载,
使得原本编写就简单的下载程序变得更加简单了。
StreamResuIt 结果类型利用 HttpServletResponse 对象返回的 ServletOutputStrcam 对象
向客户端输出下载文件的二进制数据, 它有下列参数:
- contentType
发送给 Web 浏览器的数据流的 MIME 类型( 默认是 text/plain ), 即下载文件的内容类型。
下载文件的类型,客户端向Tomcat请求静态资源的时候,Tomcat会自动在 Response Head 里面添加 “Content-Type” 属性,具体的属性列表配置 - contentLength
数据流的长度, 以字节为单位(浏览器显示一个进度栏), 即下载文件的长度 -
contentDisposition:
这个属性配置下载文件的文件名等属性,其中文件类型划分为inline、attachment两种:
1. inline:浏览器尝试直接打开文件(默认值)
2. atachment:浏览器直接下载为附件
这也不是绝对的, 对于浏览器能够显示的下载文件是这样的, 对于浏览器不支持的下载类型, 即使使用 inline 选
项, 仍然会弹出“ 文件下栽” 对话框。 contentDispostion 的默认值是 inline。
- inputName
Action 中用来下载文件的属性的名字, 该属性的类型是 InputStream。 默认认值是inputStream
inputName:用于指定下载文件的输入流入口,在Action中需要指定该输入流入口。即要求Action中定义对应属性,该属性类型为InputStream。缺省为inputStream
如果在Action中声明的是getInputStream()方法,在配置文件struts.xml中配置为<param name="inputName">inputStream</param>;
如果在Action中声明的是getTargetFile()方法,在配置文件struts.xml配置为<param name="inputName">targetFile</param>。
- bufferSize
文件数据从输入复制到输出的缓冲区的大小, 默认为 1024 字节 - allowCaching
是否允许缓存
StreamResult 结果类型已经在 struts-default 文件中定义, 逻辑名为 stream。
<result name="success" type="stream">
<param name="contentType">text/plain</param>
<param name="inputName">inputStream</param>
//此处的filename是要下载的文件显示的文件名
<param name="contentDisposition">attachment;filename="hello.txt"</param>
<param name="bufferSize">1024</param>
</result>
注意
2. 案例
<body>
<s:debug/>
<s:a href="download?fileName=a.txt">download方式一</s:a>
<a href="download?fileName=a.txt">down方式二</a>
</body>
public class DownloadAction extends ActionSupport {
private String fileName;
public InputStream getInputStream() throws IOException {
String path = ServletActionContext.getServletContext().getRealPath(fileName);
/**
* 方法一 File file = new File(path); return new FileInputStream(file);
* 方法二FileUtils.openInputStream(file);
*/
File file = new File(path);
return FileUtils.openInputStream(file);
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
@Override
public String execute() throws Exception {
return super.execute();
}
public void setInputStream(InputStream inputStream) {
}
}
<action
name="download"
class="action.DownloadAction">
<result name="success" type="stream">
<param name="contentType">text/plain</param>
<param name="inputName">inputStream</param>
<param name="contentDisposition">attachment;filename="hello.txt"</param>
<param name="bufferSize">1024</param>
</result>
</action>
此时一定不要忘记导入包,还有FileUtils到导入的包是哪个,看仔细了import org.apache.commons.io.FileUtils;
缺点或改进
缺点
- 如果你要下栽任意目录下的文件( 而不仅限于Web应用程序目录 ),那么只需要修改 action中返回文件输入流的方式即可 ,例 如return new FileInputStream(inputPath)。
- 在实际应用中, 下栽的文件不应该在 struts.xml 文件中配置, 如果是这样, 那就等于将 FileDownloadAction 的功能限制为了只下载一个文件。 下载文件的选取应该通过编码来实现, 根据浏览器传来的下载请求, 查询配置文件或数据库, 获取真实下载文件数据, 构造 inputStream 对象
- 对于下载的文件名、 下载的文件类型都是在 struts.xml 文件中配置。 如果有许多不同类型的文件要提供给用户下载, 那么这种方式显然有失灵活性。
改进
- 为了能动态设置文件类型和下载的文件名, 我们可以编写一个拦截器, 在 Action 执行完毕, result 执行之前, 动态添加 StreamResult 的参数。
- 为了能够在 result 执行之前截获调用, 我们需要用到 PreResultListener 接口, 该接口可以通过 Actionlnvocation注册, 在 Action 执行完毕, result 执行之前得到一个回调。PreResultListener 需要在拦截器中进行注册, 所以我们需要编写一个拦截器, 然后在
拦截器方法中注册一个 PreResultListener 实例。
关于下载文件的错误
-
java.lang.IllegalArgumentException: Can not find a java.io.InputStream with the name [inputStream] in the invocation stack. Check the <param name="inputName"> tag specified for this action. at org.apache.struts2.result.StreamResult.doExecute(StreamResult.java:234) ERROR DefaultDispatcherErrorHandler Exception occurred during processing request: java.lang.IllegalArgumentException: Can not find a java.io.InputStream with the name [inputStream] in the invocation stack. Check the <param name="inputName"> tag specified for this action.
这个是因为ServletActionContext.getServletContext().getResourceAsStream(path+fileName);返回的值为null造成的。
也就是说:文件的路径错误。