Spring MVC中優雅地進行文件下載以及下載文件名亂碼問題

文本轉載:http://blog.lifw.org/post/24251622。


本文介紹在 spring mvc 中如何進行文件的下載,以及關於下載文件名亂碼這個令人頭疼的問題的一些探討。

一、在 spring mvc 中進行文件下載,主要有以下步驟

1. 設置響應內容類型 Content-Type

調用 URLConnection.guessContentTypeFromName(String fileName) 方法簡單判斷一下文件的類型,如果不能識別文件類型,則使用默認類型 application/octet-stream。調用 response.setContentType(mimeType); 設置 Content-Type。


2. 設置響應長度 Content-Length

調用 response.setContentLengthLong(file.length()); 設置 Content-Length。


3. 設置 Content-Disposition 響應頭

調用 response.setHeader("Content-Disposition", "attachment;fileName=\"" + encodedFileName + "\""); 設置 Content-Disposition 響應頭。Content-Disposition 響應頭有兩個作用,一是使用 attachment 用來告訴瀏覽器進行文件下載,而不是嘗試打開文件,二是使用 fileName 來指定文件下載名。

這裏有一個十分噁心的問題,就是 fileName 只支持 ASCII 字符,如果是中文則爲亂碼,而且不同瀏覽器編碼也不同,下面將專門討論一下該問題的解決方案。


4. 使用 FileCopyUtils 進行文件下載

spring 爲我們提供了 FileCopyUtils 來進行文件流相關操作,使用該方法能大大簡化文件下載代碼。


二、文件下載名亂碼問題的一些探討

1.如果使用 URLEncoder.encode(fileName) 對文件名進行編碼,可以解決 chrome 和 ie 瀏覽器文件名亂碼問題。但是有個小問題,該方法會將空格轉換成 + ,如果文件名爲 "壁 紙.jpg",則下載後文件名就變成了 "壁+紙.jpg",因此需要將編碼後的文件名中的 + 替換成 utf-8 中空格的編碼 "%20"。


2.使用 MimeUtility.encodeWord(fileName) 方法對文件名進行編碼,可以解決 Firefox 文件名亂碼問題,但是注意將 fileName 用雙引號引起來,否則如果文件名中有空格將會導致文件名截斷問題,例如文件名爲"壁 紙.jpg",則下載後文件名變成 "壁",丟失了空格後的內容。


3.Safari 亂碼問題找了很多資料也沒解決,貌似無解。


通過以上探討,形成了以下思路解決亂碼問題,首先使用 eu.bitwalker.UserAgentUtils 工具包通過 User-Agent 來判斷瀏覽器類型,如果是 ie、chrome、Safari,則使用 URLEncoder 對文件名進行編碼,並將加號替換爲 "%20" ,如果是 firefox ,則使用 MimeUtility 對文件名進行編碼,並將文件名使用雙引號引起來。


三、參考代碼

加入 eu.bitwalker.UserAgentUtils 依賴

<dependency>
   <groupId>eu.bitwalker</groupId>
   <artifactId>UserAgentUtils</artifactId>
   <version>1.19</version>
</dependency>

文件下載代碼

方法一:

            @RequestMapping(value = "/Download", method = RequestMethod.GET)  
	    public ResponseEntity<InputStreamResource> downloadFile(HttpServletRequest request)  throws IOException { 

	        String filePath = "E:\\test\\測 試.txt";  
	        FileSystemResource file = new FileSystemResource(filePath); 
	        //文件名編碼,解決亂碼問題
	        String fileName = file.getFilename();
	        //解決文件名亂碼問題
	       // String  fileName = URLEncoder.encode(file.getFilename(), StandardCharsets.UTF_8.toString());
	        String userAgentString = request.getHeader("User-Agent");
	        String browser = UserAgent.parseUserAgentString(userAgentString).getBrowser().getGroup().getName();
	       // String t=browser;
	        if(browser.equals("Chrome") || browser.equals("Internet Exploer") || browser.equals("Safari")) {
	        	fileName = URLEncoder.encode(fileName,"utf-8").replaceAll("\\+", "%20");
	        } else {
	        	fileName = MimeUtility.decodeText(fileName) ;  //encodeWord(fileName);
	        }

	        HttpHeaders headers = new HttpHeaders();  
	        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");   
	        headers.add("Content-Disposition", String.format("attachment; filename=\"%s\"", fileName)); 
	        headers.add("Pragma", "no-cache");  
	        headers.add("Expires", "0");  
	        return ResponseEntity  
	                .ok()  
	                .headers(headers)  
	                .contentLength(file.contentLength())  
	                .contentType(MediaType.parseMediaType("application/octet-stream"))  
	                .body(new InputStreamResource(file.getInputStream()));  
	    } 
方法二:
@Controller
public class DownloadController {
 
    @RequestMapping("/download")
    public void download(HttpServletResponse response,HttpServletRequest request) throws IOException {
        File file = new File("/Music/1901.m4a");
        //判斷文件是否存在
        if(!file.exists()) {
            return;
        }
        //判斷文件類型
        String mimeType = URLConnection.guessContentTypeFromName(file.getName());
        if(mimeType == null) {
            mimeType = "application/octet-stream";
        }
        response.setContentType(mimeType);
         
        //設置文件響應大小
        response.setContentLengthLong(file.length());
         
        //文件名編碼,解決亂碼問題
        String fileName = file.getName();
        String encodedFileName = null;
        String userAgentString = request.getHeader("User-Agent");
        String browser = UserAgent.parseUserAgentString(userAgentString).getBrowser().getGroup().getName();
        if(browser.equals("Chrome") || browser.equals("Internet Exploer") || browser.equals("Safari")) {
            encodedFileName = URLEncoder.encode(fileName,"utf-8").replaceAll("\\+", "%20");
        } else {
            encodedFileName = MimeUtility.encodeWord(fileName);
        }
         
        //設置Content-Disposition響應頭,一方面可以指定下載的文件名,另一方面可以引導瀏覽器彈出文件下載窗口
        response.setHeader("Content-Disposition", "attachment;fileName=\"" + encodedFileName + "\"");
         
        //文件下載
        InputStream in = new BufferedInputStream(new FileInputStream(file));
        FileCopyUtils.copy(in, response.getOutputStream());
    }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章