文件下载 - 2877206/docs GitHub Wiki

本文介绍在 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 依赖 eu.bitwalker UserAgentUtils 1.19

文件下载代码 @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());
}

}

⚠️ **GitHub.com Fallback** ⚠️