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());
    }


猜你喜欢

转载自blog.csdn.net/u013964761/article/details/79820685