Servlet的IOException异常流字节数组

问题描述:

我偶尔从Servlet我有,为了提供文件下载功能写入字节数组的OutputStream获得一个IOException时。Servlet的IOException异常流字节数组

这个下载的servlet流量合理(每月点击10万次),这种情况很少出现,大约每月1-2次。

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

是这个IO异常被超出了我的控制应用程序什么引起的?例如 网络问题或用户重置连接?我试图谷歌在这个堆栈中的IOException的原因,但无济于事。

环境正在运行Tomcat 5.5在CentOS 5.3与Apache HTTP服务器作为使用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"); 
     } 
    } 
+0

相关主题:http://*.com/search?q=clientabortexception – BalusC 2010-06-01 23:04:20

我怀疑客户端(浏览器)的字节数组到插座的写作过程中断开。

+0

处理此问题的正确方法是什么?捕获所有IOExceptions? – 2010-06-01 21:50:49

+0

我会那样做,是的。 – 2010-06-01 21:53:28

+5

你可以抓住它,但你不能以任何明智的方式处理它。您无法恢复响应,也无法将任何位写入客户端。你可以在最高的日志。但这些信息有多有用?客户只是决定放弃下载或导航,或者网络出去了,或者它的PC崩溃了。对于统计来说很有趣,但实际上并不仅限于此。 – BalusC 2010-06-01 23:02:08

尝试搜集更多有关环境时,此异常情况,并记录它。

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

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

另外,我建议你看看commons-fileupload。即使你没有在你的项目中使用它,浏览源代码并查看他们如何下载文件。他们在文件上传过程中曾经有很多IOException,但是在更高版本中已经修复了这些问题。你可能试图弄清楚究竟是什么修复了。

+0

在Java的任何地方都没有这样的事情,'一个字节数组太大而无法在一次传递中写入'。 并且此输出流已被分块,请参阅堆栈跟踪。 – EJP 2010-06-02 03:48:52

+0

@EJP。是的,我知道,但Java抽象和真实世界是两回事。 – 2010-06-02 11:15:08

+0

无意义的评论。我再说一遍,没有太大的字节数组无法在Java中的任何地方编写。如果您有相反的证据,请提供。我们正在讨论在这里阻塞I/O,它循环直到写入完成或发生异常。 – EJP 2010-06-02 23:58:34

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

如果这不是你的应用程序的终端问题,这可能是这种情况,那么你应该抓住这个异常,什么也不做。如果您想收集关于下载被客户端中止的频率的统计信息,您可以做一些日志记录。

处理这个问题的正确方法是什么?捕获所有IOExceptions?

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

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

首先,你应该使用一个日志框架(如log4j的),而不是通过调用e.printStackTrace()写的诊断标准错误。

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

  • 该消息是无效的。
  • 客户端在此时不会期待错误消息。
  • 如果需要错误消息,则客户端将无法区分错误消息和真实数据......而无需知道所有服务器错误消息的硬件知识。

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

最后,您不能采取这种方法,通常将错误消息写入客户端以处理I/O异常。如果I/O异常指示输出连接中断,则向响应流写入消息只会引发另一个异常。