流式传输字节数组时的Servlet IOException

时间:2010-06-01 21:40:17

标签: java tomcat servlets

我偶尔会从Servlet中获取IOException,它将一个字节数组写入输出流,以便提供文件下载功能。

此下载servlet具有合理的流量(每月100次点击),此异常很少发生,大约每月1-2次。

我尝试使用完全相同的Base64字符串重新创建异常,并且不会引发异常,并且Servlet的行为与设计相同。

此IO异常是否由应用程序控制之外的内容引起?例如 网络问题或用户重置连接?我试图在堆栈的这一点上谷歌IOException的原因,但无济于事。

环境在CentOS 5.3上运行Tomcat 5.5,Apache HTTP Server充当使用proxy_ajp的代理。

ClientAbortException:  java.io.IOException

at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBufferjava:366)
    at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:352)
    at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:392)
    at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:381)
    at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:89)
    at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:83)
    at com.myApp.Download.doPost(Download.java:34)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:269)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:691)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:469)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:403)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:301)
    at com.myApp.EntryServlet.service(EntryServlet.java:278)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:269)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
    at com.myApp.filters.RequestFilter.doFilter(RequestFilter.java:16)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:210)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:172)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:151)
    at org.apache.coyote.ajp.AjpAprProcessor.process(AjpAprProcessor.java:444)
    at org.apache.coyote.ajp.AjpAprProtocol$AjpConnectionHandler.process(AjpAprProtocol.java:472)
    at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1286)
    at java.lang.Thread.run(Thread.java:636)

Caused by: java.io.IOException
    at org.apache.coyote.ajp.AjpAprProcessor.flush(AjpAprProcessor.java:1200)
    at org.apache.coyote.ajp.AjpAprProcessor$SocketOutputBuffer.doWrite(AjpAprProcessor.java:1285)
    at org.apache.coyote.Response.doWrite(Response.java:560)
    at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBufferjava:361)

下载Servlet中的代码:

@Override
    protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException {     
        try {
            response.setContentType("application/pdf");
            response.setHeader("Pragma", "");
            response.setHeader("Cache-Control", "");
            response.setHeader("Content-Disposition", "Inline; Filename=myPDFFile..pdf");
            ServletOutputStream out = response.getOutputStream();
            byte[] downloadBytes = Base64.decode((String)request.getAttribute("fileToDownloadBase64"));
            out.write(downloadBytes);
        } catch (Base64DecodingException e) {
            e.printStackTrace();
            response.getOutputStream().print("An error occurred");
        }
    }

4 个答案:

答案 0 :(得分:2)

我怀疑在将字节数组写入套接字期间客户端(浏览器)正在断开连接。

答案 1 :(得分:2)

  

处理此问题的正确方法是什么?抓住所有IOExceptions?

捕获所有IOExceptions是合理的,但以下是狡猾的:

    catch (Base64DecodingException e) {
        e.printStackTrace();
        response.getOutputStream().print("An error occurred");
    }

首先,您应该使用日志框架(例如log4j),而不是通过调用e.printStackTrace()将诊断写入stderr。

其次,编写类似于响应输出的错误消息可能是错误的。

  • 该消息没有信息。
  • 此时客户端不会收到错误消息。
  • 如果期望出现错误消息,则客户端将无法区分错误消息与实际数据...没有对所有服务器错误消息的硬连线知识。

向HTTP客户端报告错误的首选方法是在HTTP响应中设置4xx或5xx状态代码,并(理想情况下)设置错误消息。但是,只有在响应未“提交”时才能执行此操作,并且打开响应输出流会提交响应。

最后,您不能采用这种方法将错误消息写入客户端以获取I / O异常。如果I / O异常表明输出连接已断开,则向响应流写入消息将引发另一个异常。

答案 2 :(得分:1)

如果客户端断开连接(即取消下载或关闭浏览器),那么您将获得IOException

如果这不是您的应用程序的终端问题(可能是这种情况),那么您应该捕获此异常并且不执行任何操作。如果您想收集关于客户端下载频率的统计信息,您可以进行一些日志记录。

答案 3 :(得分:0)

尝试在发生此异常时收集有关环境的更多信息并记录下来。

在正常情况下记录字节数组大小和HTTP请求中的“User-Agent”字段,并在抛出异常时明确记录此信息。

可能是字节数组太大而无法一次写入,您可能需要将其分成几个块。

另外,我建议你看看commons-fileupload。即使您不在项目中使用它,也请浏览源代码并查看它们如何下载文件。他们在文件上传过程中曾经有过很多IOExceptions,但是在以后的版本中已经修复了。您可能会试图弄清楚到底修正了什么。