如何使用Apache Http从GET请求响应中直接下载Jasper Server导出的文件
问题根因
核心错误是CloseableHttpResponse被放在try-with-resources代码块中,该代码块执行完成后会自动关闭响应对象,连带绑定的响应体输入流也会被提前释放,后续调用content.getContent()读取的是已经关闭的空流,所以输出的文件大小为0KB、提示损坏。
修正方案
核心修改点
- 将读取响应体、写入客户端输出流的逻辑全部移动到
CloseableHttpResponse的try-with-resources块内部,保证流读取时响应还未被关闭 - 直接复用Apache HttpClient提供的
HttpEntity#writeTo方法简化流复制逻辑,避免手动读写缓冲区出错 - 把
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
相关产品推荐
相关产品推荐

