首页 > 编程语言 > 详细

spring-mvc文件下载出现异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response

时间:2020-03-01 19:56:04      阅读:56      评论:0      收藏:0      [点我收藏+]
最近做文件下载的功能,大概就是下载一个excel模板,前端提交表单时,请求后台下载的controller
 
发现后台逻辑结束后,后台出现异常,虽然没有影响文件下载的功能,但考虑到异常有影响程序的运行的风险,所以想办法去除这个异常。
 
异常如下:
 
java.lang.IllegalStateException: getOutputStream() has already been called for this response
    at org.apache.catalina.connector.Response.getWriter(Response.java:564)
    at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:212)
    at org.springframework.web.servlet.view.freemarker.FreeMarkerView.processTemplate(FreeMarkerView.java:366)
    at org.springframework.web.servlet.view.freemarker.FreeMarkerView.doRender(FreeMarkerView.java:283)
    at org.springframework.web.servlet.view.freemarker.FreeMarkerView.renderMergedTemplateModel(FreeMarkerView.java:233)
    at org.springframework.web.servlet.view.AbstractTemplateView.renderMergedOutputModel(AbstractTemplateView.java:167)
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:250)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1047)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:817)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at com.airport.common.web.ProcessTimeFilter.doFilter(ProcessTimeFilter.java:35)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:94)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:502)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1156)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:684)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1539)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1495)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

 

前端的结构是有一个下载使用的表单:
 
     <div class="body-box">
           <@p.form id="jvForm" action="v_import.do"  labelWidth="12"  class="form-inline"   enctype="multipart/form-data" onsubmit="return false;">
                <@p.td id="linkLogo" label="文件路径">
                      <span id="ufc0" style="position:relative">
                           <input id=‘uploadFileText0‘  name="filepath" type=‘text‘ class="btn btn-default" size="14"  readonly="true" />  
                           <input id="uploadBrower" class="browse  btn btn-default" type=‘button‘ value=‘浏览‘/>
                           <input id="uploadFile0" name="file"  onchange="$(‘#uploadFileText0‘).val(this.value);" size="14"  type="file" class="file-button btn btn-default"/>
                      </span>
                      <input id="uploadButton"  class="upload-button btn btn-default" type="button" value="导入"  />
                      <input id="templateButton" class="btn  btn-default" type="button" value="模板下载" /><br/>
                      <input id="success" type="hidden"  value="${success!}" />
                      <input id="exceptionInfo" type="hidden"  value="${exceptionInfo!‘‘}" />
                      <input id="defaultExceptionInfo"  type="hidden" value="导入失败!" />
                </@p.td>
           </@p.form>
     </div>

 

当点击上边‘模板下载‘按钮时,提交表单,js逻辑为:

<script type="text/javascript">
    $(function() {
        // 点击下载模板
        $("#templateButton").bind("click",function(){
            downloadTemplate();
        });        
    });

    /*
     * 下载证书导入数据模板
     */
    function downloadTemplate() {
        var form = document.getElementById("jvForm");
        form.action = "v_download_template.do";
        form.encoding =  "application/x-www-form-urlencoded";
        form.submit();
    }
</script>

 

下载逻辑全部在后台,前台使用freemarker及html,下载使用的后台代码:

 

controller:

    @RequestMapping("/certificate_import/v_download_template.do")
     public String downloadTemplate(HttpServletRequest request,  HttpServletResponse response) {
           log.info("下载证书导入模板...");
           File template =  cmsCertificateImportMng.getCertificateDataTemplate();
           if (template == null) {
                log.warn("没有获取到证书数据导入模板文件,下载失败");
                return "certificate_import/list";
           }
           // 将文件流写入response
           FileDownloadUtils.writeFile2Response(response,  template);
           return "certificate_import/import";
     }

 

FileDownloadUtils:
     public static void writeFile2Response(HttpServletResponse  response, File template) {
           response.setContentType("application/x-download;charset=UTF-8");
           response.addHeader("Content-disposition", "filename="  + template.getName());
           OutputStream os = null;
           ServletOutputStream ros = null;
           try {
                ros = response.getOutputStream();
                os = new BufferedOutputStream(ros);
                byte[] bs = FileUtils.toByteArray(template);
                os.write(bs);
                os.flush();
                ros.flush();
           } catch (IOException e) {
                log.error("意外的异常:", e);
           } finally {
                if (os != null) {
                      try {
                           os.close();
                      } catch (IOException e) {
                           log.error("意外的异常:", e);
                      }
                }
                if (ros != null) {
                      try {
                           ros.flush();
                           ros.close();
                      } catch (IOException e) {
                           log.error("意外的异常:", e);
                      }
                }
           }
     }

 

后来我想到,原因可能是前端请求后,后台有两个response响应:
 
第一个response是将文件写到输出流中,第二个是controller返回到原来的页面。
 
最终我找到了解决办法:controller不返回前端页面,将controller方法改为无返回参数void:
 
     @RequestMapping("/certificate_import/v_download_template.do")
     public void downloadTemplate(HttpServletRequest request,  HttpServletResponse response) {
           log.info("下载证书导入模板...");
           File template =  cmsCertificateImportMng.getCertificateDataTemplate();
           if (template == null) {
                log.warn("没有获取到证书数据导入模板文件,下载失败");
                return;
           }
           // 将文件流写入response
           FileDownloadUtils.writeFile2Response(response,  template);
     }
 
这样下载时就再没有出现过上边的异常
 
本文的后台下载代码是经过实际验证的,能够正常使用的,如有需要实现下载功能可以参考以上代码。
 

spring-mvc文件下载出现异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response

原文:https://www.cnblogs.com/xhj123/p/12391457.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!