来自servlet输出流的响应问题

时间:2011-08-31 16:36:26

标签: java jsf servlets download

在我的基于Java的Web应用程序中,我试图在ZIP文件中写一些文件,我想提示用户下载/取消/保存。下载对话框打开的时间以及如果我单击取消,之后如果我尝试访问应用程序中的任何链接,则会再次打开对话框。这是我的代码片段。

private void sendResponse(byte[] buf, File tempFile) throws IOException {
    long length = tempFile.length();
    HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
    String disposition = "attachment; fileName=search_download.zip";
    ServletOutputStream servletOutputStream = null;
    InputStream in = null; 
    try {
        if (buf != null) {
            in = new BufferedInputStream(new FileInputStream(tempFile));
            servletOutputStream = response.getOutputStream();
            response.setContentType("application/zip");
            response.setHeader("Content-Disposition", disposition);
            while ((in != null) && ((length = in.read(buf)) != -1)) {
                servletOutputStream.write(buf, 0, (int) length);
            }
        }
    } finally {
        if (servletOutputStream != null) {
            servletOutputStream.close();
        }
        if (in != null) {
            in.close();
        }
        if (tempFile != null) {
            tempFile.delete();
        }
    }
    context.responseComplete();
}

此外,一旦我点击“保存/打开”,它就会按预期工作。我希望清除响应对象的问题。请帮我提供一些解决方案。

修改 下载选择的方法

      public void downloadSelected() throws IOException {

    List<NodeRef> list = init();
    StringBuffer errors = new StringBuffer("");
    ZipOutputStream out = null;

    File tempFile = null;
    byte[] buf = null;
    try {
        if (list != null && list.size() > 0) {
            tempFile = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX_ZIP);
            out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tempFile)));
            buf = writeIntoZip(list,out);
            sendResponse(buf,tempFile);
        } else {
            errors.append("No Items Selected for Download");
            this.errorMessage = errors.toString();
        }
    } 
    catch(IOException e) {
        System.out.println("Cancelled");
    }       
}

写入Zip方法:

private byte[] writeIntoZip(List<NodeRef> list,ZipOutputStream out) throws IOException {
    String downloadUrl = "";
    InputStream bis = null;
    Node node = null;
    String nodeName = "";
    byte[] buf = null;
    Map<String,Integer> contents = new HashMap<String, Integer>();
    ContentReader reader = null;
    for (NodeRef nodeRef : list) {
        try {
            node = new Node(nodeRef);
            nodeName = node.getName();
            reader = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getContentService().getReader(nodeRef, ContentModel.PROP_CONTENT);
            bis = new BufferedInputStream(reader.getContentInputStream());
            if (bis != null) {
                contents = setFiles(contents,nodeName);
                nodeName = getUniqueFileName(contents, nodeName);
                buf = new byte[4 * 1024];
                buf = writeOutputStream(bis).toByteArray();
                out.putNextEntry(new ZipEntry(nodeName));
                out.write(buf);
            }
        } catch (Exception e) {
            e.printStackTrace();
            if(out != null) {
                out.close();
            }
        }
    }
    if(out != null) {
        out.close();
    }
    return buf;
}

谢谢, Jeya

1 个答案:

答案 0 :(得分:1)

我当然不确定这个问题的根本原因。基于到目前为止发布的代码,无法解释此行为。 SSCCE肯定会有所帮助。但我发现了这个问题的几个潜在原因。也许修复其中一个或全部将解决具体问题。

  1. 您已将JSF的FacesContext指定为bean的属性。这是错误,如果它是一个范围比请求范围更广泛的bean。它应该始终FacesContext#getCurrentInstance()的本地方法范围内获取。它返回一个线程局部变量,永远不应该在其他请求之间共享。也许你已经将bean放在会话范围内,并且已经设置了标题的前一个请求的悬空response对象将被重用。

  2. 您没有抓住IOException方法close()。如果客户端取消下载,则servletOutputStream.close()将抛出IOException,表示客户端已中止响应。在您的情况下,in将不再关闭,tempFile将不再被删除,JSF响应将不再完成。您还应该捕获close()并记录/忽略该异常,以便确保finally完成其工作。也许tempFile的存在会对您未来的POST操作产生影响。

  3. 您正在使用<h:commandLink>代替<h:outputLink><h:link>或普通<a>进行页面到页面的导航。这使用POST代替GET,这对于用户体验和SEO来说是糟糕。您应该使用GET链接而不是POST链接。另请参阅When should I use h:outputLink instead of h:commandLink?