在Spring Boot应用中优雅地处理下载文件的名称

对于文件的下载功能来说,我归纳为2头一流。极其简单。

  • Content-Type 头,告诉客户端文件类型
  • Content-Disposition 头,告诉客户端对于这个文件的处理方式
  • Output 流,写入文件内容到客户端

文件名称乱码的问题

Content-Disposition 有三个属性值,可以指定文件的名称。

  • name
  • filename
  • filename*

至于区别,我也说不大清楚。反正是有个规范,我感觉很乱。具体可以参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition

这就是容易翻车的地方了,由于文件名称设置方法不正确。或者是浏览器不兼容问题,导致中文文件名称乱码的事儿常有。例如下面的代码:

response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + "中文の文件.txt");

直接拼接中文名称,在谷歌浏览器中下载就是乱码。

常见的解决方案

以下我是在网上看到的一些解决方案。

  1. 对文件名称进行URI编码

    response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode("中文の文件.txt", StandardCharsets.UTF_8));
    
  2. 修改字符编码

    response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + new String("中文の文件.txt".getBytes("GBK"),"ISO-8859-1"));
    

经过测试,上面2种办法都OK。

比较优雅的方式

有多优雅呢?就是Spring给你提供了这么一个工具类: org.springframework.http.ContentDisposition

这东西很方便。不需要自己对文件名称做修改。它会自动帮我们处理好一切。

ContentDisposition 使用实在是太简单了,这里就不多说了,给一个完整的使用示例就能看明白。

package io.springboot.demo.web.controller;


import java.io.IOException;
import java.nio.charset.StandardCharsets;

import javax.servlet.http.HttpServletResponse;

import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/demo")
public class DemoController {
	
	@GetMapping
	public void demo (HttpServletResponse response) throws IOException {

		byte[] content = "Hello Spring".getBytes(StandardCharsets.UTF_8);
		
		// 文件类型
        response.setContentType("text/plain");
        // 文本类型文件的编码
        response.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
        // 文件长度
        response.setContentLength(content.length);
        // 文件的处理方式。 attachment 表示附件,filename 表示文件的名称
		response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition
															.attachment() 			// 附件形式
															.filename("中文の文件.txt", StandardCharsets.UTF_8)  // 文件名称 & 编码
															.build()
															.toString());
        response.getOutputStream().write(content);
	}
}

以后文件下载接口,不要自己去处理文件名称了。交给 ContentDisposition 吧。

2 个赞

我他吗直接疯狂的学习 :hot_face: :hot_face: :hot_face: :hot_face: :hot_face: :hot_face: :hot_face: