SpringBoot文件下载
前言
文件下载,常用的一个功能,比较简单,但是有可能会遇见一些问题
- 文件太大,若写法上有问题,可能会导致内存溢出
- 文件中文名称乱码问题
方式1:HttpServletResponse.write
直接将文件一次性读取到内存中,然后通过response.write()写入到客户端,这种方式适合小文件,若文件比较大,将文件一次性到内存中可能导致OOM,需要注意1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void download(HttpServletResponse response) throws IOException {
// 指定要下载的文件
File file = ResourceUtils.getFile("classpath:测试文件1.txt");
// 文件转成字节数组
byte[] fileBytes = Files.readAllBytes(file.toPath());
// 文件名编码,防止中文乱码
String fileName = URLEncoder.encode(file.getName(), "UTF-8");
// 设置响应头信息
response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
// 内容类型为通用类型,表示二进制数据流
response.setContentType("application/octet-stream");
// 输出文件内容
try(OutputStream os = response.getOutputStream()){
os.write(fileBytes);
}
}
方式2:ResponseEntity
方法需要返回ResponseEntity类型的对象,这个类是SpringBoot中自带的,是对http相应结果的一种封装,可以用来构建http响应结果:包含响应状态码、响应头、响应体等信息1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public ResponseEntity<byte[]> download2() throws IOException {
// 指定要下载的文件
File file = ResourceUtils.getFile("classpath:测试文件1.txt");
// 文件转成字节数组
byte[] fileBytes = Files.readAllBytes(file.toPath());
// 文件名编码,防止中文乱码
String fileName = URLEncoder.encode(file.getName(), "UTF-8");
// 构建响应实体:ResponseEntity, 包含了http请求的响应信息,比如状态码、响应头、响应体等信息
ResponseEntity<byte[]> responseEntity = ResponseEntity.ok()
// 设置响应头信息
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=\"" + fileName + "\"")
// 内容类型为通用类型,表示二进制数据流
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)
// 响应体
.body(fileBytes);
return responseEntity;
}
上面两种方式,都是直接将文件读取到内存字节数组中,然后输出到客户端,这种方式适合小文件,若文件比较大,将文件一次性到内存中导致OOM
方式3:通用方案(适合任意文件大小),建议采用
Resource是spring中的一个资源接口,是对资源的一种抽象,常见的几个实现类
- ClassPathResource:表示类路径下的资源,即src/main/resources目录下的资源
- Resource resource = new ClassPathResource(“测试文件1.txt”);
- FileSystemResource:表示文件系统下的资源,即磁盘上的文件
- Resource resource = new FileSystemResource(“D:\测试文件1.txt”);
- UrlResource:表示网络资源,即http://www.baidu.com 等
- InputStreamResource:表示输入流资源,即通过输入流获取的资源
- ByteArrayResource:表示字节数组资源,即通过字节数组获取的资源
边读边写,边读边写,避免OOM
1 |
|
返回ResponseEntity
1 |
|
中文乱码问题
文件名中如果有中文,下载下来后文件名称是乱码,解决代码如下,需要对文件名称进行编码
1
String fileName = URLEncoder.encode(file.getName(), "UTF-8");
但是使用URLEncoder.encode()可能不符合HTTP标准(RFC 5987),部分浏览器可能仍会乱码。
- 改进方案:使用RFC 5987规范编码文件名
1
2
3String encodedFileName = "filename*=UTF-8''" +
URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20");
response.setHeader("Content-Disposition", "attachment; " + encodedFileName);
- 改进方案:使用RFC 5987规范编码文件名
核心响应头分类及作用
Content-Type
- 作用:告诉客户端响应体的数据格式(MIME 类型)
- 常见场景:
- 返回 JSON 数据:
application/json
- 返回 HTML 页面:
text/html
- 文件下载:
application/octet-stream
(通用二进制流)或具体类型(如 image/png/jpeg/pdf等)
- 返回 JSON 数据:
- 设置方法:
1
2//response.setHeader("Content-Type", "application/json");
response.setContentType("application/json; charset=UTF-8");- 注意事项:
- 必须包含字符编码(如 charset=UTF-8),否则可能乱码。
- 若未设置,浏览器可能根据内容猜测类型(MIME 嗅探),存在安全风险。
Content-Disposition
- 作用:控制客户端如何处理响应内容(如直接展示或下载为文件)
- 常见场景:
- 强制文件下载:
attachment; filename="file.txt"
- 内联显示:
inline
(如直接在浏览器中显示图片)- 注意:如果在请求头中设置了
response.setHeader("Content-Type", "image/png");
会默认以内联方式显示,而不需要显式设置inline
- 注意:如果在请求头中设置了
- 强制文件下载:
- 设置方法:
1
2//response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
Cache-Control
- 作用:控制客户端和代理服务器的缓存行为。
- 常见场景:
- 禁止缓存:
no-store
- 缓存但需验证:
no-cache
- 缓存有效期:
max-age=3600
(缓存 1 小时)
- 禁止缓存:
- 设置方法:
1
response.setHeader("Cache-Control", "no-store");
Location
- 作用:重定向客户端到新的 URL(需配合 3xx 状态码)。
- 常见场景:
- 用户登录后跳转到主页
- 旧 URL 迁移到新地址
- 设置方法:
1
2response.setStatus(HttpServletResponse.SC_FOUND); // 302
response.setHeader("Location", "/new-url");
Set-Cookie
- 作用:向客户端设置 Cookie。
- 常见场景:
- 用户会话管理(如 JSESSIONID)
- 记住用户偏好设置
- 设置方法:
1
2
3Cookie cookie = new Cookie("theme", "dark");
cookie.setMaxAge(86400); // 有效期 1 天
response.addCookie(cookie);
X-Content-Type-Options
- 作用:向客户端设置 Cookie。
- 设置方法:
1
response.setHeader("X-Content-Type-Options", "nosniff");
其他实用响应头
- CORS(跨域资源共享)
1
2
3response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST");
response.setHeader("Access-Control-Allow-Headers", "Content-Type"); - 安全头
1
2
3
4
5
6// 防止点击劫持
response.setHeader("X-Frame-Options", "DENY");
// 防止 XSS 攻击
response.setHeader("X-XSS-Protection", "1; mode=block");
// 禁止嗅探 MIME 类型
response.setHeader("X-Content-Type-Options", "nosniff");
经典使用场景
返回JSON数据
1 |
|
关键头:
- Content-Type: application/json; charset=UTF-8
- X-Content-Type-Options: nosniff(可选,增强安全)
文件下载
1 |
|
关键头:
- Content-Type: application/pdf(明确文件类型)
- Content-Disposition: attachment; filename*=UTF-8’’%E6%96%87%E4%BB%B6.pdf(强制下载并解决中文乱码)
重定向
1 |
|
关键头:
- Location: /new-page
- HTTP Status: 301 或 302
禁用缓存(敏感数据)
1 | response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); |
关键头:
- Cache-Control: no-store
- Pragma: no-cache(兼容旧浏览器)
- Expires: 0(立即过期)