报错:getOutputStream() has already been called for this response

错误信息详情:

严重: Servlet.service() for servlet [jsp] in context with path [/exportExcel] threw exception [java.lang.IllegalStateException: getOutputStream() has already been called for this response] with root cause
java.lang.IllegalStateException: getOutputStream() has already been called for this response
    at org.apache.catalina.connector.Response.getWriter(Response.java:678)
    at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:213)
    at org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:125)
    at org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:118)
    at org.apache.jasper.runtime.PageContextImpl.release(PageContextImpl.java:194)
    at org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:126)
    at org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:80)
    at org.apache.jsp.export_jsp._jspService(export_jsp.java:566)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
    ...

问题背景

在写一个 download.jsp ,提供下载文件功能(部分关键代码)如下:

....

<%
    ...
    response.addHeader("Content-Disposition","attachment;filename=\"..\"");
    response.addHeader("Content-Length",43408);
    ...
    OutputStream out = response.getOutputStream();
    InputStream in;
    //todo intialize inputStream
    ...
    
    while((n = in.read(b)) != -1) {
        out.write(b, 0, n);
    }
    out.flush;
%>

运行结果:

结果功能 -> 正常,但是有一点不爽!!日志一直有报错,报错如下:

java.lang.IllegalStateException: getOutputStream() has already been called for this response

分析

翻阅了挺多资料,最后总结如下:

  1. 其实就是因为 JSP 中同时用了 response.getOutputStream() 以及 JSP 内置对象 out.write().
  2. Serlvet 规范中不能同时使用这两个。只能选其中一个。所以再来分析代码:
    1. response.getOutputStream() : 很明显在代码中已经用到了。
    2. out.write():这个很有可能是 JSP 中存在空行或者回车导致的。至于到底是什么时候有 out.write() 。大家可以到 tomcat 目录下看看 jsp 生成的 .java 文件就一目了然了。

解决方案

参考网上的,在调用 response.getOutStream() 之前清空一下两者的缓存。同时更新 out 的内容。

详情看代码(实践已证明可行)↓

<%
    // 同时 reset response 和 清空 out
    response.reset();
    out.clear();
    out = pageContext.pushBody();

    // ---------------------增加上面的内容--------------------------------------
    ...
    response.addHeader("Content-Disposition","attachment;filename=\"..\"");
    response.addHeader("Content-Length",43408);
    ...
    OutputStream out = response.getOutputStream();
    InputStream in;
    //todo intialize inputStream
    ...
    
    while((n = in.read(b)) != -1) {
        out.write(b, 0, n);
    }
    out.flush;
%>

你可能感兴趣的:(报错:getOutputStream() has already been called for this response)