struts2 下载文件
2016-01-14 18:40
561 查看
本来下载文件这事,我还想自己写个方法去实现的。但是想到项目使用到了struts2框架,而struts2中的“StreamResult”这个result恰好就是用来下载的,所以,何必自再写呢。呵呵。
首先,我们看一下“org.apache.struts2.dispatcher.StreamResult”类的JAVADOC,如下:
根据doc来配置我们的result。如下:
对于上面的Result的写法不明白的话,可以参照我的另一篇BLOG(param 中的参数下面会有说明):
http://rainbow702.iteye.com/blog/2218653
假设页面链接如下:
那么,我们在UserAction中,须存在一个名为 dlManual() 的方法,其如下:
上面有一点需要注意的是: 使用 getResourceAsStream() 去加载文件,传给它的路径须以“/”开头。
这个时候,我们在看一眼上面定义好的Result,其中它的“inputName”的值为“manualInputStream”,这样一来,我们须在action类中定义一个名为 manualInputStream 的InputStream字段。同时声明它的getter/setter方法。
另外,Result中的“contentDisposition”的值被声明为“attachment;fileName=\"${manualName}\"”,重点是“${manualName}”,它会被当作ONGL表达式进行解析,并在当前action中寻找一个名为manualName的字段(实际上是寻找名为 getManualName() 的方法, 这一点对于会反射的人应该理解起来没有问题)。
所以,我们需要声明一个 getManualName() 方法(当然你也可以声明一个 String类型的名称为 manualName 的字段,并声明它的getter方法)。
以上有一处需要注意:需要使用URLEncoder对文件名进行encode,不然的话,中文会有问题。
另外,encode 会把空格解析成 “+”,而IE不会被这个“+”还原为初始的空格,所以我们要手动将“+”替换为“%20”。
OK,只需要如上来做就可以进行下载文件了。
测试环境 :
Chrome40
IE10
360 版本7.1 内核 31.1
以下附上 StreamResult 的关键代码,其实就跟我们自己写的下载文件的代码是一样的:
首先,我们看一下“org.apache.struts2.dispatcher.StreamResult”类的JAVADOC,如下:
A custom Result type for sending raw data (via an InputStream) directly to the HttpServletResponse. Very useful for allowing users to download content. This result type takes the following parameters: ★ contentType - the stream mime-type as sent to the web browser (default = text/plain). ★ contentLength - the stream length in bytes (the browser displays a progress bar). ★ contentDisposition - the content disposition header value for specifing the file name (default = inline, values are typically attachment;filename="document.pdf". ★ inputName - the name of the InputStream property from the chained action (default = inputStream). ★ bufferSize - the size of the buffer to copy from input to output (default = 1024). ★ allowCaching if set to 'false' it will set the headers 'Pragma' and 'Cache-Control' to 'no-cahce', and prevent client from caching the content. (default = true) ★ contentCharSet if set to a string, ';charset=value' will be added to the content-type header, where value is the string set. If set to an expression, the result of evaluating the expression will be used. If not set, then no charset will be set on the header These parameters can also be set by exposing a similarly named getter method on your Action. For example, you can provide getContentType() to override that parameter for the current action.
根据doc来配置我们的result。如下:
@Result(name = "dlManual", type = "stream", params = { "contentType", "application/msword", "contentDisposition", "attachment;fileName=\"${manualName}\"", "inputName", "manualInputStream", "bufferSize", "2048" }) })
对于上面的Result的写法不明白的话,可以参照我的另一篇BLOG(param 中的参数下面会有说明):
http://rainbow702.iteye.com/blog/2218653
假设页面链接如下:
<a href="<%=request.getContextPath()%>/user!dlManual">download</a>
那么,我们在UserAction中,须存在一个名为 dlManual() 的方法,其如下:
public String dlManual() { LOG.debug("dlManual() start."); try { InputStream is = request.getServletContext().getResourceAsStream("/xxxx/xxxxxx.doc"); this.setManualInputStream(is); } catch (Exception e) { LOG.error(e.getMessage(), e); } finally { LOG.debug("dlManual() end."); } return "dlManual"; }
上面有一点需要注意的是: 使用 getResourceAsStream() 去加载文件,传给它的路径须以“/”开头。
这个时候,我们在看一眼上面定义好的Result,其中它的“inputName”的值为“manualInputStream”,这样一来,我们须在action类中定义一个名为 manualInputStream 的InputStream字段。同时声明它的getter/setter方法。
private InputStream manualInputStream; //getter //setter
另外,Result中的“contentDisposition”的值被声明为“attachment;fileName=\"${manualName}\"”,重点是“${manualName}”,它会被当作ONGL表达式进行解析,并在当前action中寻找一个名为manualName的字段(实际上是寻找名为 getManualName() 的方法, 这一点对于会反射的人应该理解起来没有问题)。
所以,我们需要声明一个 getManualName() 方法(当然你也可以声明一个 String类型的名称为 manualName 的字段,并声明它的getter方法)。
public String getManualName() { LOG.debug("getManualName() start."); String manualName = "xxxxx.doc"; try { // the " " will be encoded as "+", // and IE will treat this "+" as a literal string, not the original " ", so here we replace "+" with "%20", manualName = URLEncoder.encode(manualName, Const.ENCODING_UTF8).replaceAll("\\+", "%20"); } catch (UnsupportedEncodingException e) { LOG.error(e.getMessage(), e); } LOG.debug("getManualName() start, manualName: " + manualName); return manualName; }
以上有一处需要注意:需要使用URLEncoder对文件名进行encode,不然的话,中文会有问题。
另外,encode 会把空格解析成 “+”,而IE不会被这个“+”还原为初始的空格,所以我们要手动将“+”替换为“%20”。
OK,只需要如上来做就可以进行下载文件了。
测试环境 :
Chrome40
IE10
360 版本7.1 内核 31.1
以下附上 StreamResult 的关键代码,其实就跟我们自己写的下载文件的代码是一样的:
protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { // Override any parameters using values on the stack resolveParamsFromStack(invocation.getStack(), invocation); OutputStream oOutput = null; try { if (inputStream == null) { // Find the inputstream from the invocation variable stack inputStream = (InputStream) invocation.getStack().findValue(conditionalParse(inputName, invocation)); } if (inputStream == null) { String msg = ("Can not find a java.io.InputStream with the name [" + inputName + "] in the invocation stack. " + "Check the <param name=\"inputName\"> tag specified for this action."); LOG.error(msg); throw new IllegalArgumentException(msg); } // Find the Response in context HttpServletResponse oResponse = (HttpServletResponse) invocation.getInvocationContext().get(HTTP_RESPONSE); // Set the content type if (contentCharSet != null && ! contentCharSet.equals("")) { oResponse.setContentType(conditionalParse(contentType, invocation)+";charset="+contentCharSet); } else { oResponse.setContentType(conditionalParse(contentType, invocation)); } // Set the content length if (contentLength != null) { String _contentLength = conditionalParse(contentLength, invocation); int _contentLengthAsInt = -1; try { _contentLengthAsInt = Integer.parseInt(_contentLength); if (_contentLengthAsInt >= 0) { oResponse.setContentLength(_contentLengthAsInt); } } catch(NumberFormatException e) { if (LOG.isWarnEnabled()) { LOG.warn("failed to recongnize "+_contentLength+" as a number, contentLength header will not be set", e); } } } // Set the content-disposition if (contentDisposition != null) { oResponse.addHeader("Content-Disposition", conditionalParse(contentDisposition, invocation)); } // Set the cache control headers if neccessary if (!allowCaching) { oResponse.addHeader("Pragma", "no-cache"); oResponse.addHeader("Cache-Control", "no-cache"); } // Get the outputstream oOutput = oResponse.getOutputStream(); if (LOG.isDebugEnabled()) { LOG.debug("Streaming result [" + inputName + "] type=[" + contentType + "] length=[" + contentLength + "] content-disposition=[" + contentDisposition + "] charset=[" + contentCharSet + "]"); } // Copy input to output if (LOG.isDebugEnabled()) { LOG.debug("Streaming to output buffer +++ START +++"); } byte[] oBuff = new byte[bufferSize]; int iSize; while (-1 != (iSize = inputStream.read(oBuff))) { oOutput.write(oBuff, 0, iSize); } if (LOG.isDebugEnabled()) { LOG.debug("Streaming to output buffer +++ END +++"); } // Flush oOutput.flush(); } finally { if (inputStream != null) inputStream.close(); if (oOutput != null) oOutput.close(); } }
相关文章推荐
- struts2 跳转至404 页面的解决方案
- Springmvc的框架流程
- struts2 通过下拉框来手动切换国际化
- struts2 拦截器Interceptor中取得request、response
- eclipse 使用adb命令
- Eclipse的 debug 介绍与技巧
- Java Swing
- java线程的两种创建方式
- java 处理json字符串中null值
- 通过注解的方式集成Spring 4 MVC+Hibernate 4+MySQL+Maven,开发项目样例
- Java 子类构造对象
- java file模糊匹配某文件夹下的文件并删除
- eclipse十大快捷键!
- zTree实现异步加载数据(使用SPRING MVC4+mybatis3.8)
- Java 日志管理最佳实践
- @RequestParam和@RequestBody的区别-------springMVC
- java中遍历MAP的几种方法
- Java六种排序算法
- struts2中,在使用 convention 插件的情况下,如何使用 “chain” 这个result
- 【第十章】集成其它Web框架 之 10.4 集成JSF ——跟我学spring3