`
Rainbow702
  • 浏览: 1066245 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
阅读更多

本来下载文件这事,我还想自己写个方法去实现的。但是想到项目使用到了struts2框架,而struts2中的“StreamResult”这个result恰好就是用来下载的,所以,何必自再写呢。呵呵。

 

首先,我们看一下“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();
        }
    }

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics