Сервлет IOException при потоковой передаче байтового массива - PullRequest
4 голосов
/ 02 июня 2010

Иногда я получаю IOException от имеющегося у меня сервлета, который записывает байтовый массив в выходной поток, чтобы обеспечить возможность загрузки файла.

Этот сервлет загрузки имеет разумный объем трафика (100 тыс. Обращений в месяц), и это исключение встречается редко, примерно 1-2 раза в месяц.

Я попытался воссоздать исключение, используя точно такую ​​же строку Base64, и исключение не вызывается, а сервлет ведет себя так, как задумано.

Это исключение ввода-вывода вызвано чем-то вне контроля моих приложений? Например проблема в сети или пользователь сбрасывает соединение? Я попытался найти причину IOException в этой точке стека, но безрезультатно.

Среда работает на Tomcat 5.5 в CentOS 5.3 с HTTP-сервером Apache, выполняющим роль прокси-сервера с использованием 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)

И код в сервлете загрузки:

@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 ]

2 голосов
/ 02 июня 2010

Как правильно обращаться с этим? Поймать все исключения IOException?

Уловка всех исключений IOException является разумной, но следующее является хитрым:

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

Во-первых, вы должны использовать каркас журналирования (например, log4j), а не записывать диагностику в stderr, вызывая e.printStackTrace().

Во-вторых, запись сообщения об ошибке, например, в ответный вывод, вероятно, неверна.

  • Это сообщение неинформативно.
  • Клиент не будет ожидать сообщения об ошибке в этот момент.
  • Если он ожидает сообщения об ошибке, клиент не сможет отличить сообщение об ошибке от реальных данных ... без аппаратного знания всех сообщений об ошибках сервера.

Предпочтительный способ сообщить об ошибках клиенту HTTP - установить код состояния 4xx или 5xx в ответе HTTP и (в идеале) установить сообщение об ошибке. Но вы можете сделать это только в том случае, если ответ не «зафиксирован», и открытие потока вывода ответа фиксирует ответ.

Наконец, вы не можете использовать этот подход при записи сообщения об ошибке клиенту для исключений ввода-вывода в целом. Если исключение ввода-вывода означало, что выходное соединение разорвано, запись сообщения в поток ответов просто вызовет другое исключение.

2 голосов
/ 02 июня 2010

Я подозреваю, что клиент (браузер) отключается во время записи байтового массива в сокет.

1 голос
/ 02 июня 2010

Если клиент отключается (т.е. отменяет загрузку или закрывает браузер), тогда вы получите IOException.

Если это не является проблемой терминала для вашего приложения, что, вероятно, имеет место, то вы должны поймать это исключение и ничего не делать. Возможно, вы могли бы заняться ведением журнала, если хотите собрать статистику о том, как часто загрузка прерывается клиентом.

0 голосов
/ 02 июня 2010

Попробуйте собрать больше информации об окружающей среде, когда произойдет это исключение, и зарегистрируйте ее.

В нормальных условиях регистрируйте размер байтового массива и поле «User-Agent» из HTTP-запроса и обязательно записывайте эту информацию при возникновении исключения.

Возможно, байтовый массив слишком велик для записи за один проход, и вам может потребоваться разбить его на несколько частей.

Кроме того, я рекомендую вам посмотреть commons-fileupload . Даже если вы не используете его в своем проекте, просмотрите исходный код и посмотрите, как они загружают файл. Раньше у них было много IOException во время загрузки файла, но они были исправлены в более поздних версиях. Вы можете попытаться выяснить, что именно было исправлено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...