此响应已经调用了getOutputStream()方法。

67
我谷歌了错误信息getOutputStream() has already been called for this response,很多人说这是由于<%%>后面有空格或换行符引起的,但在我的代码中,没有空格或换行符。我正在使用Linux上的Tomcat6。
<%@
    page import="java.servlet.*,
    javax.servlet.http.*,
    java.io.*,
    java.util.*,
    com.lowagie.text.pdf.*,
    com.lowagie.text.*"
    %><%
    response.setContentType("application/pdf");
    Document document = new Document();
    try{
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        PdfWriter.getInstance(document, buffer);
        document.open();
        PdfPTable table = new PdfPTable(2);
        table.addCell("1");
        table.addCell("2");
        table.addCell("3");
        table.addCell("4");
        table.addCell("5");
        table.addCell("6");
        document.add(table);
        document.close();
        DataOutput dataOutput = new DataOutputStream(response.getOutputStream());
        byte[] bytes = buffer.toByteArray();
        response.setContentLength(bytes.length);
        for(int i = 0; i < bytes.length; i++)
        {
        dataOutput.writeByte(bytes[i]);
        }
    }catch(DocumentException e){
        e.printStackTrace();
    }

%>

~

org.apache.jasper.JasperException: java.lang.IllegalStateException: getOutputStream() has already been called for this response
    org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:522)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:410)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:342)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:267)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
根本原因
java.lang.IllegalStateException: getOutputStream() has already been called for this response
    org.apache.catalina.connector.Response.getWriter(Response.java:610)
    org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:198)
    org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:125)
    org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:118)
    org.apache.jasper.runtime.PageContextImpl.release(PageContextImpl.java:188)
    org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:118)
    org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:77)
    org.apache.jsp.Account.Domain.testPDF_jsp._jspService(testPDF_jsp.java:94)
    org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:374)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:342)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:267)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
16个回答

59

好的,你应该使用一个servlet而不是JSP,但如果你真的需要...在你的页面顶部添加这个指令:

<%@ page trimDirectiveWhitespaces="true" %>

或者在你的web.xml文件的jsp-config部分中

<jsp-config>
  <jsp-property-group>
    <url-pattern>*.jsp</url-pattern>
    <trim-directive-whitespaces>true</trim-directive-whitespaces>
  </jsp-property-group>
</jsp-config>

完成后还需要flush / closeOutputStream,然后返回。

dataOutput.flush();
dataOutput.close();
return;

3
你已经验证过这个是否有效吗?我怀疑它不会有任何影响,因为无论页面内容是什么,容器还是会调用getWriter - skaffman
不,如果在<% %>之外绝对没有空格,则不会影响。但这可能是特定于实现的行为,毕竟我不会依赖它。据我所知,在Tomcat中它应该可以正常工作。但是,Java代码应该放在Java类中,而不是jsp文件中。就这样。 - BalusC
我忘了提到trimDirectiveWhitespaces指令适用于JSP 2.1或更高版本。 - RealHowTo
@BalusC:原始示例中<% %>外没有空格,但这并没有产生影响。 - skaffman
2
这对我没有修复错误,但确实修复了我的大量空白。 - zmanc
显示剩余2条评论

40

这里的问题是您的 JSP 直接与响应 OutputStream 进行交互。技术上并不禁止这样做,但这绝不是一个好主意。

具体来说,您调用 response.getOutputStream() 并向其中写入数据。稍后,当 JSP 引擎尝试刷新响应时,它会失败,因为您的代码已经“占用”了响应。一个应用程序可以在任何给定的响应上调用 getOutputStreamgetWriter,但不能同时进行。JSP 引擎使用 getWriter,所以您不能调用 getOutputStream

您应该将此代码编写为 Servlet,而不是 JSP。JSP 只适合作为包含在 JSP 中的文本输出。您可以看到您的 JSP 中实际上没有文本输出,它只包含 Java 代码。


1
整个项目基于JSP,我只需要添加PDF下载功能。 - Southsouth
4
抱歉,您不能这样做。JSP适用于文本而非二进制格式。 - skaffman
4
Java代码应该放在Java类中,JSP页面应该只使用标签库/EL表达式。如果标签库/EL表达式无法实现,则应使用Servlet。 - BalusC
3
北虎,即使项目完全由JSP组成,你仍然可以添加一个Servlet。一旦编译器处理完它们,JSP最终也会变成servlet。如果要处理PDF文件,使用普通的servlet会更好。 - Kris
3
我同意你们使用Servlet,但Servlet需要更多的配置。 - Southsouth

10

我在第二次导出时遇到了这个问题。一旦我添加了:

response.getOutputStream().flush();
response.getOutputStream().close();

在导出操作完成后,我的代码始终正常运行。


JSP想要打开它以打印HTML内容,因此它会在你的最后一个%>之后打开。 - ern0

9
将以下内容添加到try/catch结尾处,以避免JSP引擎通过getWriter()刷新响应时出现的错误。
out.clear(); // where out is a JspWriter
out = pageContext.pushBody();

正如已经注意到的那样,这不是最佳实践,但它可以避免日志中的错误。

3
我刚刚遇到了这个问题。
产生这个问题的原因是我的控制器方法在退出时尝试返回字符串(视图名称)类型。当该方法退出时,第二个响应流就会被启动。
将控制器方法的返回类型更改为void可以解决这个问题。
如果还有其他人遇到这个问题,希望这可以帮到你。

没有返回类型为void的函数也遇到了同样的问题。 - Durga

3

以下是我在类似情况下的解决方案。

当你完成向Servlet OutputStream写入内容后,只需调用response.sendRedirect("yourPage.jsp");即可。这将导致浏览器发起一个新请求,因此避免向同一输出流中写入内容。


2

JSP是一个展示框架,通常不应包含任何程序逻辑。正如Skaffman所建议的那样,使用纯servlet或任何MVC web框架来实现您想要的内容。


0

我没有使用JSP,但是当我通过调用PrintWriter的flush()方法或return语句来设置返回JSON对象的响应时,我遇到了类似的错误。之前的答案即将return语句包装在try块中似乎有点奏效:错误消失了,因为return语句使方法忽略了try-catch下面的所有代码,特别是在我的情况下,redirectStrategy.sendRedirect(request, response, destination_addr_string)这一行似乎修改了已经提交的响应,导致了错误。在我的情况下,更简单的解决方案是删除该行,并让客户端应用程序处理重定向。


0

在使用request.getRequestDispatcher(path).forward(request, response);之前,我使用response.getWriter()时也遇到了同样的错误。因此,当我将其替换为response.getOutputStream()时,开始工作正常。


0

这个错误出现在我的程序中,因为结果集调用了比数据库中包含的更多的列来显示在PDF文档中。例如,表包含30个字段,但程序却调用了35个(resultset.getString(35))


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接