JSP文件下载及出现getOutputStream() has already been called for this response的解决方法

本文介绍了两种在JSP中实现文件下载的方法:一种是通过RequestDispatcher转发请求来完成下载;另一种是利用文件流直接输出文件内容。同时针对getOutputStream()hasalreadybeencalledforthisresponse错误提供了详细的解决思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JSP文件下载及出现getOutputStream() has already been called for this response的解决方法

http://iamin.blogdriver.com/iamin/1072546.html

一、采用RequestDispatcher的方式进行

1、web.xml文件中增加
  <mime-mapping>
    <extension>doc</extension>
    <mime-type>application/vnd.ms-word</mime-type>
  </mime-mapping>


2、程序如下:

<%@page language="java" import="java.net.*" pageEncoding="gb2312"%>
<%
    response.setContentType("application/x-download");//设置为下载application/x-download
    String filenamedownload = "/系统解决方案.doc";//即将下载的文件的相对路径
    String filenamedisplay = "系统解决方案.doc";//下载文件时显示的文件保存名称
    filenamedisplay = URLEncoder.encode(filenamedisplay,"UTF-8");
    response.addHeader("Content-Disposition","attachment;filename=" + filenamedisplay);
   
    try
    {
        RequestDispatcher dispatcher = application.getRequestDispatcher(filenamedownload);
        if(dispatcher != null)
        {
            dispatcher.forward(request,response);
        }
        response.flushBuffer();
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }
    finally
    {
   
    }
%>


二、采用文件流输出的方式下载

1、web.xml文件中增加
  <mime-mapping>
    <extension>doc</extension>
    <mime-type>application/vnd.ms-word</mime-type>
  </mime-mapping>


2、程序如下:

<%@page language="java" contentType="application/x-msdownload" import="java.io.*,java.net.*" pageEncoding="gb2312"%><%
    //关于文件下载时采用文件流输出的方式处理:
    //加上response.reset(),并且所有的%>后面不要换行,包括最后一个;
    //因为Application Server在处理编译jsp时对于%>和<%之间的内容一般是原样输出,而且默认是PrintWriter,
    //而你却要进行流输出:ServletOutputStream,这样做相当于试图在Servlet中使用两种输出机制,
    //就会发生:getOutputStream() has already been called for this response的错误
    //详细请见《More Java Pitfill》一书的第二部分 Web层Item 33:试图在Servlet中使用两种输出机制 270
    //而且如果有换行,对于文本文件没有什么问题,但是对于其它格式,比如AutoCAD、Word、Excel等文件
    //下载下来的文件中就会多出一些换行符0x0d和0x0a,这样可能导致某些格式的文件无法打开,有些也可以正常打开。

    response.reset();//可以加也可以不加
    response.setContentType("application/x-download");//设置为下载application/x-download
    // /../../退WEB-INF/classes两级到应用的根目录下去,注意Tomcat与WebLogic下面这一句得到的路径不同,WebLogic中路径最后没有/
    System.out.println(this.getClass().getClassLoader().getResource("/").getPath());
    String filenamedownload = this.getClass().getClassLoader().getResource("/").getPath() + "/../../系统解决方案.doc";
    String filenamedisplay = "系统解决方案.doc";//系统解决方案.txt
    filenamedisplay = URLEncoder.encode(filenamedisplay,"UTF-8");
    response.addHeader("Content-Disposition","attachment;filename=" + filenamedisplay);

    OutputStream output = null;
    FileInputStream fis = null;
    try
    {
        output  = response.getOutputStream();
        fis = new FileInputStream(filenamedownload);

        byte[] b = new byte[1024];
        int i = 0;

        while((i = fis.read(b)) > 0)
        {
            output.write(b, 0, i);
        }
        output.flush();
    }
    catch(Exception e)
    {
        System.out.println("Error!");
        e.printStackTrace();
    }
    finally
    {
        if(fis != null)
        {
            fis.close();
            fis = null;
        }
        if(output != null)
        {
            output.close();
            output = null;
        }
    }
%>

### 关于 `getOutputStream()` 被调用后的响应错误解决方案 当遇到 `java.lang.IllegalStateException: getOutputStream() has already been called for this response` 的异常时,通常意味着在同一请求处理过程中尝试多次获取同一响应的输出流或字符输出流。这违反了Servlet API的规定,在Java Web应用程序中是一个常见的编程陷阱。 #### 原因分析 该问题的根本原因是试图在一个HTTP响应对象上先后调用 `getWriter()` 和 `getOutputStream()` 方法,而这两个方法只能择一使用,并且一旦选择了其中一个,则不能再访问另一个[^1]。此外,如果在Spring框架环境中操作复杂的数据结构(如列表),并将其直接作为依赖注入到控制器或其他组件中,可能会间接引发此类冲突[^5]。 #### 解决策略 ##### 避免重复调用 确保在整个请求生命周期内仅调用一次 `getOutputStream()` 或者 `getWriter()` 。可以通过重构代码逻辑来实现这一点: ```java if (conditionForBinaryData) { try(OutputStream out = response.getOutputStream()) { // 使用try-with-resources语句自动关闭资源 byte[] data = ...; // 准备二进制数据 out.write(data); } } else { PrintWriter writer = response.getWriter(); String content = "Some text"; writer.print(content); } ``` ##### 处理复杂的业务场景 对于涉及大量计算或者需要返回动态生成文件的情况,考虑创建新的实例而不是共享同一个服务层的对象。特别是当这些对象包含了难以序列化的成员变量时,更应该如此做。例如,可以手动实例化所需的工具类而非依赖容器管理其生命周期。 ##### 清晰定义MIME类型 设置正确的Content-Type头可以帮助浏览器正确解析服务器发送过来的信息。如果不小心设置了不匹配的内容类型,也可能触发此异常。因此建议显式指定预期格式: ```java response.setContentType("application/pdf"); // 或者其他合适的媒体类型 ``` ##### 捕获潜在的竞争条件 有时并发环境下多个线程可能同时修改相同的HttpServletResponse实例,从而造成竞争状态。应采取措施防止这种情况发生,比如同步关键部分的操作或是利用ThreadLocal机制隔离各次请求间的上下文环境。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值