You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Apache Http从GET请求响应中直接下载Jasper Server导出的文件

问题根因

核心错误是CloseableHttpResponse被放在try-with-resources代码块中,该代码块执行完成后会自动关闭响应对象,连带绑定的响应体输入流也会被提前释放,后续调用content.getContent()读取的是已经关闭的空流,所以输出的文件大小为0KB、提示损坏。

修正方案

核心修改点

  1. 将读取响应体、写入客户端输出流的逻辑全部移动到CloseableHttpResponse的try-with-resources块内部,保证流读取时响应还未被关闭
  2. 直接复用Apache HttpClient提供的HttpEntity#writeTo方法简化流复制逻辑,避免手动读写缓冲区出错
  3. 把CloseableHttpClient也纳入try-with-resources管理,自动释放连接资源,不需要手动调用close避免资源泄漏

修正后代码

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {      
    String username = request.getParameter("username");
    String pass = request.getParameter("pass");
    
    String fileType = "pdf"; // request.getParameter("type") 可选pdf、xls等
            
    // httpClient纳入try-with-resources自动管理,无需手动关闭
    try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
        CookieStore cookieStore = new BasicCookieStore();
        HttpContext httpContext = new BasicHttpContext();
        httpContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
        
        List<NameValuePair> urlParameters = new ArrayList<>();
        urlParameters.add(new BasicNameValuePair("j_username",username));
        urlParameters.add(new BasicNameValuePair("j_password",pass));   
        
        boolean valid = executeAuthenticationValidation(urlParameters,response,httpClient,httpContext);
        
        if(valid) {
            ReportObject repObj = requestJasperReport(request.getParameter("params"),fileType,response,httpClient,httpContext);
            
            if(repObj != null) {
                String requestId = repObj.requestId;
                String exportId = repObj.exports.get(0).id;
                HttpGet get = new HttpGet("http://localhost:8081/jasperserver/rest_v2/reportExecutions/"+requestId+"/exports/"+exportId+"/outputResource");

                // 所有响应操作都在该try块内完成,不提前关闭响应
                try (CloseableHttpResponse chres = httpClient.execute(get,httpContext)) {
                    StatusLine status = chres.getStatusLine();
                    int rescod = status.getStatusCode();
                    if(rescod==200) {
                        String contentDisposition = chres.getFirstHeader("Content-Disposition").getValue().split(";")[1];
                        String contentType = chres.getFirstHeader("Content-Type").getValue();
                        HttpEntity content = chres.getEntity();

                        response.setContentType(contentType);
                        response.setHeader("Content-Disposition", contentDisposition);
                        // 可选:设置内容长度,方便浏览器显示下载进度
                        response.setContentLengthLong(content.getContentLength());

                        // 直接将 JasperServer 返回的响应体写入客户端输出流,无本地落地
                        content.writeTo(response.getOutputStream());
                        response.getOutputStream().flush();
                    }
                }
            }           
        } else {
            // 处理认证失败逻辑
        }
    }
}

可选优化点

  • 对Content-Disposition中的文件名做URL编码处理,避免中文文件名乱码
  • 增加请求超时配置,避免长时间无响应卡住服务线程
  • 新增异常捕获逻辑,响应下载失败的错误提示给前端

内容的提问来源于stack exchange,提问作者TwoDent

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 17:27:01