2010-06-01 5 views
4

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

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

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

Является ли это исключение IO причиной чего-то вне моего управления приложениями? Например, проблема сети или пользователь, перезагружающий соединение? Я попытался объяснить причину исключения 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) 

И код в 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"); 
     } 
    } 
+0

Связанные темы: http://stackoverflow.com/search?q=clientabortexception – BalusC

ответ

2

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

+0

Каков правильный способ справиться с этим? Поймать все IOExceptions? –

+0

Я бы сделал это, да. –

+5

Вы можете поймать его, но вы не можете справиться с ним разумным образом. Вы не можете отменить ответ и больше не пишите бит клиенту. Вы можете в самом высоком состоянии регистрировать его. Но насколько полезной эта информация? Клиент просто решил прервать загрузку или уйти, или сеть была отключена, или ее компьютер разбился. Забавно для статистики, но на самом деле не более того. – BalusC

0

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

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

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

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

+0

Нет такой вещи, чтобы массив байтов был слишком большим, чтобы быть написанным за один проход в любом месте Java. И этот выходной поток уже разбивается, см. Трассировку стека. – EJP

+0

@EJP. Да, я знаю, но Java-абстракция и реальный мир - это две разные вещи. –

+0

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

1

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

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

2

Каков правильный способ справиться с этим? Поймать все IOExceptions?

Ловля все IOExceptions разумно, но следующее хитроумный:

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

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

Во-вторых, написание сообщения об ошибке, похожее на ответ, вероятно, неверно.

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

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

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

Смежные вопросы