java.lang.IllegalStateException: Already using output stream分析及處理辦法
錯誤型別大致為以下幾種:
java.lang.IllegalStateException:Cannot forward a response that is already committed
IllegalStateException:response already commited
IllegalStateException:getOutputStream() has already been called for this request
錯誤原因:該異常表示,當前對客戶端的響應已經結束,不能在響應已經結束(或說消亡)後再向客戶端(實際上是緩衝區)輸出任何內容。
具體分析:
首先解釋下flush(),我們知道在使用讀寫流的時候資料先被讀入記憶體這個緩衝區中,然後再寫入檔案,但是當資料讀完時不代表資料已經寫入檔案完畢,因為可能還有一部分仍未寫入檔案而留在記憶體中,這時呼叫flush()方法就會把緩衝區的資料強行清空輸出,因此flush()的作用就是保證快取清空輸出。response是服務端對客戶端請求的一個響應,其中封裝了響應頭、狀態碼、內容等,服務端在把response提交到客戶端之前,會向緩衝區內寫入響應頭和狀態碼,然後將所有內容flush。這就標誌著該次響應已經committed(提交)。對於當前頁面中已經committed(提交)
JDK API:
① flushBuffer
public void flushBuffer()throws IOException
Forces any content in the buffer to be written to the client. A call to this method automatically commits the response, meaning the status code and headers will be written.
② sendRedirect
public void sendRedirect(String location)throws IOException
Sends a temporary redirect response to the client using the specified redirect location URL. This method can accept relative URLs; the servlet container must convert the relative URL to an absolute URL before sending the response to the client. If the location
is relative without a leading '/' the container interprets it as relative to the current request URI. If the location is relative with a leading '/' the container interprets it as relative to the servlet container root.
If the response has already been committed, this method throws an IllegalStateException. After using this method, the response should be considered to be committed and should not be written to.
③ forward
public void forward(ServletRequest request,ServletResponse response) throws ServletException,IOException Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server. This method allows one servlet to do preliminary
processing of a request and another resource to generate the response.
For a RequestDispatcher obtained via getRequestDispatcher(), the ServletRequest object has its path elements and parameters adjusted to match the path of the target resource.
forward should be called before the response has been committed to the client (before response body output has been flushed). If the response already has been committed, this method throws an IllegalStateException. Uncommitted output in the response buffer
is automatically cleared before the forward.
The request and response parameters must be either the same objects as were passed to the calling servlet's service method or be subclasses of the ServletRequestWrapper or ServletResponseWrapper classes that wrap them.
備註:注:在一次響應commit之前,所有的內容輸出都將寫入servlet引擎的緩衝區(tomcat或weblogic的內容空間),而在commit之後,上一次response向緩衝區寫入的內容,將清空。由於servlet在沒有設定單執行緒的情況下(使用Single-Threaded
Model,servlet實現SingleThreadModel介面,jsp使用<%@ page isThreadSafe="false" %>),是多執行緒的,所以上面所說的緩衝區,都將是該response所屬的執行緒私有的記憶體空間。有了這個概念,將可以分析碰到的關於servlet多執行緒的很多問題。如果不能確認response是否已經committed. 可以呼叫response.isCommitted()來判斷。導致這個錯誤最普遍的原因是,jsp有編譯錯誤。常見解決辦法:
1、 response.sendRedirect()方法後加return語句即可,如下:
response.sendRedirect("login.jsp");
return;
2、 查提交的url是否有誤。
3、如果你的頁面中用了清快取程式碼response.flushbuffer();又用到了response.sendRedirect(url);
你可以把response.flushbuffer();去掉,或者用JS的window.location.href="url";來做轉向。
④如果你用了OutputStream,而web容器生成的servlet程式碼中有out.write(””),這個和JSP中呼叫的
response.getOutputStream()衝突。out.write()這個是字元流,而response.getOutputStream()是位元組流,你不能在同一個頁面中呼叫多個輸出流。無論先呼叫哪一個,在呼叫第二個時都會丟擲IllegalStateException,因為在jsp中,out變數是通過response.getWriter得到的。在多個使用了 outputStream的<%%>語句之間不能有空格及多餘的字元。也就是頁面中除了使用了outputStream的<%%>之外不能有空格或其它任何字元,在之內的語句可以有空格及回車。
在JSP頁面做輸出的時候有兩種方式.一是通過JspWriter,另一個是通過OutputStream,但二者互相排斥.如果並存的話就會報告以上異常.在不得不使用OutputStream的時候.我們必須要把JspWriter捨棄掉了。找到請求異常的頁面所對應的Servlet..把其中所有使用JspWriter的語句全部去掉.或者是到你的JSP檔案裡把動態輸出的程式碼註釋掉.這裡注意換行和空格製表符均為JspWriter輸出.應該一起去掉.儲存檔案重新啟動伺服器你會發現上述異常消失了。 由於jsp container在處理完成請求後會呼叫releasePageContet方法釋放所用的PageContext object,並且同時呼叫getWriter方法,由於getWriter方法與在jsp頁面中使用流相關的getOutputStream方法衝突,所以會造成這種異常,解決辦法是:只需要在jsp頁面的最後加上兩條語句:
out.clear();
out=pageContext.pushBody();
即可(其中out,pageContext均為jsp內建物件!) 。
轉自:http://my.oschina.net/guhai2004/blog/187041