java Spring MVC 控制器代码:已经为此响应调用了 getOutputStream()

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12325382/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 08:26:42  来源:igfitidea点击:

Spring MVC Controller code: getOutputStream() has already been called for this response

javaspringexceptionspring-mvccsv

提问by Raevik

I'm trying to assemble a CSV file from some DB data and send it to the client from my Spring MVC controller (and associated services). I'm using SuperCSV to handle the writing and output: http://supercsv.sourceforge.net/

我正在尝试从一些数据库数据组装一个 CSV 文件,并将它从我的 Spring MVC 控制器(和相关服务)发送到客户端。我正在使用 SuperCSV 来处理写入和输出:http://supercsv.sourceforge.net/

So, here's the controller method:

所以,这是控制器方法:

@RequestMapping(value="/getFullReqData.html", method = RequestMethod.GET)
public void getFullData(HttpSession session, HttpServletRequest request, HttpServletResponse response) throws IOException{
    logger.info("INFO:  ******************************Received request for full Req data dump");

    List<Requirement> allRecords = reqService.getFullDataSet(ProjectService.getProjectID((String)session.getAttribute("currentProject")));
    response.setContentType("text/csv;charset=utf-8"); 
    response.setHeader("Content-Disposition","attachment; filename=nistData.csv");
    OutputStream fout= response.getOutputStream();  
    OutputStream bos = new BufferedOutputStream(fout);   
    OutputStreamWriter outputwriter = new OutputStreamWriter(bos); 

    ICsvBeanWriter writer = new CsvBeanWriter(outputwriter, CsvPreference.EXCEL_PREFERENCE);
    try {
      final String[] header = new String[] { 
              "ReqID", 
              "ColName1",
              "ColName2",
              "ColName3",
              "ColName4",
              "ColName5", 
              "ColName6",
              "ColName7",
              "ColName8",                                 
              "ColName9",
              "ColName10" };

      // the actual writing
      writer.writeHeader(header);

      for(Requirement aCR : allRecords){
          writer.write(aCR, header);
      }

    } finally {
        writer.close();
    }
};

This is actually moving forward and getting the response to the client browser which triggers the download of the csv file.

这实际上是向前推进并获得对触发 csv 文件下载的客户端浏览器的响应。

Two problems:

两个问题:

1) I'm getting a pile of server-side exception spewage (below)

1)我得到一堆服务器端异常喷水(下)

2) The CSV file has only the headers in it and no data, despite the allRecords list showing as fully populated in the debugger.

2) CSV 文件中只有标题,没有数据,尽管 allRecords 列表在调试器中显示为完全填充。

I'm hoping the failure-to-populate is caused by the exception, but I'm suspicious. Exception trace:

我希望填充失败是由异常引起的,但我很怀疑。异常跟踪:

SEVERE: Servlet.service() for servlet jsp threw exception
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:633)
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:214)
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:105)
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:105)
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:182)
at org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:123)
at org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:80)
at org.apache.jsp.error_jsp._jspService(error_jsp.java:102)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)

The exception looks to get passed along as I'm seeing it 4 times per invocation of this method.

异常看起来会被传递,因为我每次调用此方法都会看到它 4 次。

All documentation I've found has people who are embedding their response.getOutputStream() inside scriptlets instead of being in a servlet. The method I've got up above is inside the Spring MVC controller, so the advise of "move it to a servlet!" doesn't seem to apply here. From what I can tell, I'm only invoking the response.getOutputStream() one time, and the method is blowing up on the first call.

我发现的所有文档都有人将他们的 response.getOutputStream() 嵌入到 scriptlet 中,而不是在 servlet 中。我上面提到的方法是在 Spring MVC 控制器内部,所以建议“将它移到一个 servlet!” 似乎不适用于这里。据我所知,我只调用了 response.getOutputStream() 一次,并且该方法在第一次调用时就崩溃了。

I'm stumped. Help?

我难住了。帮助?

回答by Biju Kunjummen

Your flow is probably raising an exception somewhere in the request mapped method - probably in writer.write(aCR, header);which then goes to the finally block, closes the response stream, then the exception bubbles up to the servlet, which tries to handle it by an error page(check your web.xml error-pagetags) , which because the response stream is closed cannot render the error jsp cleanly and hence the exception message you are seeing.

您的流程可能在请求映射方法中的某处引发异常 - 可能在writer.write(aCR, header);其中转到 finally 块,关闭响应流,然后异常冒泡到 servlet,该 servlet 尝试通过错误页面处理它(检查您的web.xmlerror-page标签) ,因为响应流已关闭,因此无法干净地呈现错误 jsp,因此无法呈现您看到的异常消息。

It will be good if you can catch the exception within the controller and print the stack trace to see what could be going wrong in the controller while writing to the output stream.

如果您可以在控制器中捕获异常并打印堆栈跟踪以查看在写入输出流时控制器中可能出现的问题,那就太好了。

回答by Solubris

Can you make it work with printWriter instead:

你能不能让它与 printWriter 一起工作:

    response.setContentType("application/vnd.ms-excel;charset=UTF-8");
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + ".xls\"");
    PrintWriter out = response.getWriter();