我偶尔会从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");
}
}
答案 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,但是在以后的版本中已经修复了。您可能会试图弄清楚到底修正了什么。