Spring MVC 文件下载及中文文件名乱码解决

内容参考 Paul Deck 著的《Spring MVC学习指南1 2版》

将文件这样的资源发送到浏览器,需要在controller中完成以下工作:

  1. 在请求的处理方法中使用void返回类型,并在方法中添加HttpServletResponse
  2. 设置Content-Type, Content-Type就是常说的MIME类型,将响应的内容类型设为文件的内容类型。Content-Type header 在某个实体的body中定义数据类型, 包含media type(媒体类型)和subtype identifiers(子类标识符)。访问http://www.iana.org/assignments/media-types 以了解更多的标准内容类型(standard content types)。如果不清楚如何设置Content-Type,要达到下载资源的效果,可以将Content-Type设置为application/octet-stream(不区分大小写)。
  3. 设置一个 HTTP response header: Content-Disposition, 值为attachment; filename=fileName, 这个fileName就是下载文件后的文件名,这个文件名有可能会和这里设置fileName不一样。

上码:
方式1:读取文件作为FileInputStream, 将内容加载到一个字节数组。随后获取HttpServletResponse的OutputStream,并调用write方法传入字节数组。
方式2:使用Java NIO's Files.copy()方法:

Path file = Paths.get(...);
Files.copy(file, response.getOutputStream());

这样代码少、而且运行得更快。

看码吧:

@Controller
public class DownloadController {   
    @RequestMapping(value="/download-resource")
    public void downloadResource(HttpServletRequest servletRequest, HttpServletResponse response) {
       // 文件保存在/WEB_INF/data目录下 
        String dataDirectory = request.
                getServletContext().getRealPath("/WEB-INF/data");
        File file = new File(dataDirectory, "sunny.pdf");
        if (file.exists()) {
            response.setContentType("application/pdf");
            response.addHeader("Content-Disposition", 
                    "attachment; filename=sunny.pdf");
            byte[] buffer = new byte[1024];
            FileInputStream fis = null;
            BufferedInputStream bis = null;
            // if using Java 7, use try-with-resources
            try {
                fis = new FileInputStream(file);
                bis = new BufferedInputStream(fis);
                OutputStream os = response.getOutputStream();
                int i = bis.read(buffer);
                while (i != -1) {
                    os.write(buffer, 0, i);
                    i = bis.read(buffer);
                }
            } catch (IOException ex) {
                // do something, 
                // probably forward to an Error page
            } finally {
                if (bis != null) {
                    try {
                        bis.close();
                    } catch (IOException e) {
                    }
                }
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                    }
                }
            }
        }
    }

    @RequestMapping(value="/download-resource1")
    public void downloadResource(HttpServletRequest servletRequest, HttpServletResponse response) {
        String dataDirectory = request.
                getServletContext().getRealPath("/WEB-INF/data");
        Path file = Paths.get(dataDirectory, "sunny.pdf");
        if (Files.exists(file)) {
            response.setContentType("application/pdf");
            String fileName = "sunny.pdf";
            response.addHeader("Content-Disposition", 
                    "attachment; filename=" + fileName);
/*
如果文件名有中文的话,进行URL编码,让中文正常显示
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
*/
            try {
                Files.copy(file, response.getOutputStream());
            } catch (IOException ex) {
            }
        }
    }
}

针对中文文件名下载后乱码分析:

上面有说

response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));

这行代码是能解决中文文件名乱码问题的,但在Safari浏览器下下载的中文文件名还是乱码的,下面提供一个解决方案,看代码:

@RequestMapping(value = "/faq")
    public void downloadFAQPdf(HttpServletResponse response) throws Exception {
        String fileName = "帮助说明.pdf";
// 获取classpath下的文件
        URL url = UserController.class.getClassLoader().getResource("data/" + fileName);
        Path file = Paths.get(url.toURI());

        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM.toString());

// 解决中文文件名乱码关键行 
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"; filename*=utf-8''" + fileName);

        Files.copy(file, response.getOutputStream());
    }

具体原因我就不细说了,参看下面这个链接(有详细的描述):
http://ju.outofmemory.cn/entry/133400

你可能感兴趣的:(Spring MVC 文件下载及中文文件名乱码解决)