2015-02-23 110 views
3

在我公司的網站上,我們有一些需要導出到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

回答

1

你可以嘗試在你的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

0

控制器將等待響應之前的響應發送回客戶端寫入。

這裏是一個不錯的職位有多種方法/選項概述

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

0

我面臨着同樣的問題。代碼不工作對我來說是

@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