Java Spring - 动态生成的csv文件下载响应挂起

问题描述:

在我公司的网站上,我们有一些需要导出到csv文件的表格。
有一些不同的参数,因此需要根据请求动态创建csv文件。Java Spring - 动态生成的csv文件下载响应挂起

我的问题是,单击下载后,响应会挂起,并等待创建整个文件(可能需要一些时间),然后才会立即下载整个文件。

我使用AngularJS,所以我使用window.location = <url_for_file_download>为了使浏览器下载文件。

在服务器端,我使用Java Spring,并且遵循了我可以在网上找到的所有说明来创建文件下载控制器。

我的控制器代码是这样的:

@RequestMapping(value = "http://yada.yada.yada/csv/myFile.csv", method = RequestMethod.GET) 
public @ResponseBody 
void getCustomers(HttpServletResponse response, 
         @RequestParam(required = false) String someParameters) 
         throws NotAuthorizedException, IOException { 
// set headers 
setHeaders(response); 
// generate writer 
CSVWriter write = generateWriter(response); 
// get data 
List<String[]> data = getData(); 
// write and flush and all that 
. 
. 
. 
} 

我设置应答头码是:

response.setContentType("text/csv;charset=utf-8"); 
response.setHeader("Content-Disposition","attachment; filename=\"" + fileName + ".csv\""); 

我也尝试添加以下标题:

response.setHeader("Transfer-Encoding", "Chunked"); 
response.setHeader("Content-Description", "File Transfer"); 

我也尝试将内容类型设置为"application/octet-stream"

请注意,我不添加Content-length标头,因为该文件尚不存在,并且正在写入。

对于写作我使用OpenCSV csv文件,我的代码如下:

OutputStream resOs = response.getOutputStream(); 
OutputStream buffOs = new BufferedOutputStream(resOs); 
OutputStreamWriter outputWriter = new OutputStreamWriter(buffOs,"UTF-8"); 
CSVWriter writer = new CSVWriter(outputWriter); 

我遍历数据,并将其写入像这样:

for (String[] row: data) { 
    writer.writeNext(line); 
} 

(这不完全代码 - 但这更否则代码会发生什么)

,并在结束时,我刷新并关闭:

writer.flush(); 
writer.close(); 

我也尝试在每行写入后刷新。

那么为什么不是在文件全部被写入之前被传输? 为什么我的浏览器(Google Chrome)在长时间等待后立即下载文件?我该如何解决这个问题。

我希望我已经添加了足够的代码,如果有什么缺失,请告诉我,我会尝试在这里添加它。

非常感谢你提前。

+0

是设置响应,并在控制器写入responseoutput流的码? – 2015-02-23 09:01:50

+0

@PaulJohn是的,抱歉,如果不明确。但我确实使用了一些帮助函数,我将httpServletResponse对象传递给它,以便设置标题并创建编写器。我将编辑该问题。 – 2015-02-23 09:05:15

+0

@jonnybordo您是否找到了解决此问题的解决方案?我有同一个。 – medvedev1088 2016-12-22 14:34:00

你可以尝试在你的java中返回一个空值吗

return null;

或者你也可以尝试下面的代码 1。在点击提交按钮jQuery代码

$(document).ready(function() { 
    $('#buttonName').click(function(e){ 
    $("#formName").submit(); 
    //alert("The file ready to be downloaded"); 

}); 
}); 

控制器代码

@RequestMapping(value="/name",method=RequestMethod.POST)     


public ModelAndView downloadCSV(ModelMap model,HttpSession session,@ModelAttribute(value="Pojo") Pojo pojo 
      ,HttpServletRequest request,HttpServletResponse response){ 

----------------some code---------------- 

response.setContentType("application/csv"); 
("application/unknown"); 
response.setHeader("content-disposition","attachment;filename =filename.csv"); 
ServletOutputStream writer = response.getOutputStream(); 
      logger.info("downloading contents to csv"); 

      writer.print("A"); 
      writer.print(','); 
      writer.println("B");    

     for(int i=0;i<limit;i++){ 

       writer.print(""+pojo.get(i).getA()); 
       writer.print(','); 
       writer.print(pojo.get(i).getB()); 
       writer.println(); 
     } 

     writer.flush(); 
     writer.close(); 

---------------some code----------- 
return null; 
} 

希望这有助于

+0

谢谢托马斯。我实际上试过返回一个null的ModelAndView,但这没有帮助。关于JQuery,我更喜欢避免使用它,因为我使用的是AngularJS。我也尝试通过将src设置为csv文件生成的隐藏iframe来下载文件。 – 2015-02-23 09:12:45

+0

一个简单的问题。你下载的csv文件大小是多少(以MB为单位)。从你的文章中,我明白你正在从数据库表中提取要下载的数据。因此,当您手动执行该查询时,需要多长时间来检索整个记录 – Thomas 2015-02-23 10:07:45

+0

这就是事实,我无法预先检测到文件大小,因为我正在生成文件。目前我正在处理200-300 K的小文件,但将来他们可以增长到MB。取决于数据。 – 2015-02-23 10:12:42

控制器将等待响应之前的响应发送回客户端写入。

这里是一个不错的职位有多种方法/选项概述

Downloading a file from spring controllers

关于定期刷新输出,以帮助固定的下载这篇文章的会谈。

how to download large files without memory issues in java

如果你正在尝试做的是让用户知道该文件下载正在进行和即将到期,我想一个Ajax进度状态indicaor可能是您的解决方案。

  1. 触发到后端的AJAX调用,同时文件被生成的服务器侧

  2. 一次响应是可用的,文件生成文件

  3. 显示进度指示器向用户呈现给用户。

我认为这里正在探索download file with ajax() POST Request via Spring MVC

希望这有助于类似的东西!

感谢, 保罗

+0

Hi Paul 。谢谢,我真的看过那篇文章,其实我已经非常使用他们的建议了,但正如你所说,控制器正在等待响应被完全写入,然后再发回,这就是我的问题。这个文件在被写入的时候被发送我不确定我在那篇文章中看到了一个解决方案 – 2015-02-23 09:35:18

+0

HI Johnny,曾经提交的响应不能被修改,你的最终目标是试图更快地下载文件吗?你真的需要能够“排序”文件吗? – 2015-02-23 10:33:30

+0

嗨保罗,我的问题是,该文件可能很大,所以它将需要一些时间来下载(我说的是10或者也许更多秒)。我的问题是因为文件立即下载,所以没有指示用户文件被请求。这就是为什么我要流式传输文件的原因,因此它将立即启动,用户将在浏览器的下载页面中看到进度。 – 2015-02-23 11:59:10

我面临着同样的问题。代码不工作对我来说是

@RequestMapping(value = "/test") 
public void test(HttpServletResponse response) 
      throws IOException, InterruptedException { 
    response.getOutputStream().println("Hello"); 
    response.getOutputStream().flush(); 
    Thread.sleep(2000); 
    response.getOutputStream().println("How"); 
    response.getOutputStream().flush(); 
    Thread.sleep(2000); 
    response.getOutputStream().println("are"); 
    response.getOutputStream().flush(); 
    Thread.sleep(2000); 
    response.getOutputStream().println("you"); 
    response.getOutputStream().flush(); 
} 

罪魁祸首是ShallowEtagHeaderFilter。当启用此过滤器时,响应将以一个块发送。当这个过滤器被混淆时,响应以多个块发送。

从这个线程Tomcat does not flush the response buffer它看起来像另一个可能的罪魁祸首可能是GzipFilter

+1

谢谢@ medvedev1088! 这可能只是我需要的解决方案。 但是,我在以前的工作中遇到过这个问题,所以无法检查它是否适用于我工作的环境。我希望我可以在某处测试此解决方案,并接受您的答案。我将尽我所能。谢谢! – 2016-12-25 16:25:46