开发者

Servlet IOException when streaming a Byte Array

I occasionally get an IOException from a Servlet I have that writes a byte array to the outputstream in order to provide a file download capability.

This download servlet has a reasonable amount of traffic (100k hits a month) and this exception occurs rarely, around 1-2 times a month.

I have tried recreating the exception by using the exact same Base64 String and no exception is raised and the Servlet behaves as designed.

Is this IO Exception being caused by something outside my applications control? For example a network issue or the user resetting the connection? I have tried to google the cause of an IOException at this point in the stack but to no avail.

The environment is running Tomcat 5.5 on CentOS 5.3 with an Apache HTTP Server acting as a proxy using 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:120开发者_Go百科0)
    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)

And the code in the Download 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");
        }
    }


What is the proper way to handle this? Catch all IOExceptions?

Catching all IOExceptions is reasonable, but the following is dodgy:

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

First, you should be using a logging framework (e.g. log4j) rather than writing diagnostics to stderr by calling e.printStackTrace().

Second, writing an error message like to the response output is probably wrong.

  • That message is uninformative.
  • The client won't be expecting an error message at that point.
  • If it is expecting an error message, the client won't be able to distinguish an error message from real data ... without hard-wired knowledge of all of the server error messages.

The preferred way to report errors to an HTTP client is to set a 4xx or 5xx status code in the HTTP response, and (ideally) set an error message. But you can only do this if the response has not "committed", and opening the response output stream commits the response.

Finally, you cannot take that approach with writing error message to the client for I/O exceptions in general. If the I/O exception was to indicate that the output connection is broken, writing a message to the response stream will just throw another exception.


I suspect the client (browser) is disconnecting during the writing of the byte array to the socket.


If the client disconnects (i.e. cancels the download or closes the browser) then you will get an IOException.

If this is not a terminal problem for your application which is probably the case then you should catch this exception and do nothing. You could perhaps do some logging if you want to gather statistics about how often downloads are aborted by the client.


Try to collect more information about environment when this exception happens and log it.

Under normal conditions log byte array size and the "User-Agent" field from the HTTP request, and definitely log this information when exception is thrown.

It may be that the byte array is too large to be written in a single pass and you may need to chop it up in a few chunks.

Also, I recommend you to look at commons-fileupload. Even if you don't use it in your project, browse the source code and see how they download the file. They used to have a lot of IOExceptions during file upload, but those were fixed in later versions. You may try to figure out what exactly was fixed.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜