文件下载

文件下载核心知识点笔记

一、核心问题与解决方向

  • 中文文件名乱码:需对文件名进行 URL 编码。
  • 大文件内存溢出(OOM):采用分块读取(InputStreamResource 包装流)。
  • 资源泄漏:使用 try-with-resources 自动关闭 IO 资源。
  • 响应头配置:通过 Content-DispositionContentTypeContentLength 控制下载行为。

二、关键技术点

1. 中文文件名编码

使用 URLEncoder.encode(filename, "UTF-8") 对中文文件名编码,避免浏览器下载时乱码。

2. 分块读取(防止 OOM)

通过 InputStreamResource 包装文件输入流,由框架自动处理分块传输,避免大文件一次性加载到内存。

3. 响应头配置

响应头 / 属性 作用 示例值
Content-Disposition 控制文件下载行为,attachment 表示作为附件下载,需指定文件名。 attachment; filename=美女.jpg
ContentType 指定响应内容的媒体类型,二进制文件通用 APPLICATION_OCTET_STREAM MediaType.APPLICATION_OCTET_STREAM
ContentLength 指定文件的字节大小,便于前端感知下载进度。 文件实际字节数

三、Spring MVC 实现示例(优化版)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.InputStreamResource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

@RestController
public class DownloadController {

@RequestMapping("/download")
public ResponseEntity<InputStreamResource> downloadFile() throws IOException {
// 1. 定义文件和原始文件名
File file = new File("D:\\Aimg\\MaYi.jpg");
String originalFilename = "美女.jpg";

// 2. 解决中文文件名乱码
String encodedFilename = URLEncoder.encode(originalFilename, StandardCharsets.UTF_8);

// 3. 分块读取 + 资源自动关闭(try-with-resources 语法)
try (FileInputStream inputStream = new FileInputStream(file)) {
InputStreamResource resource = new InputStreamResource(inputStream);

return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(file.length()) // 准确的文件大小
.header("Content-Disposition", "attachment; filename=" + encodedFilename)
.body(resource);
}
}
}

四、关键优化说明

  • 资源安全try-with-resources 确保文件输入流自动关闭,避免资源泄漏。
  • 性能可靠InputStreamResource 分块传输大文件,彻底解决 OOM 风险。
  • 兼容性强URLEncoder 处理中文文件名,兼容各类浏览器。
  • 参数准确file.length() 保证 ContentLength 准确,提升下载体验。

通过以上配置,可实现安全、高效、兼容的文件下载功能,覆盖中文乱码、大文件处理、资源管理等核心场景。

==idea代码:==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.example.springmvc.controller;

import com.example.springmvc.bean.Person;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;

@RestController // 相当于@Controller+@ResponseBody
public class ResponseTestController {

@RequestMapping("/resp01")
public Person resp01() {
Person person = new Person();
person.setUsername("张三");
person.setPassword("1111");
person.setCellphone("222222");
person.setAgreement(false);
person.setSex("男");
person.setHobby(new String[]{"足球", "篮球"});
person.setGrade("一年级");
return person;
}

/**
* 文件下载:
* HttpEntity:拿到整个请求数据
* ResponseEntity:拿到整个响应数据(响应头,响应体,状态码)
*
* @return
*
*/
@RequestMapping("/download")
public ResponseEntity<byte[]> download() throws IOException {
FileInputStream inputStream = new FileInputStream("D:\\Aimg\\MaYi.jpg");
// 一口气读会溢出
byte[] bytes = inputStream.readAllBytes();

// 1.文件名中文会乱码
// 2.文件太大会导致 内存溢出
// 解决:
// 1.URLEncoder.encode(filename,"UTF-8"):解决中文乱码问题
// 2.分片下载:解决内存溢出问题

String encode = URLEncoder.encode("美女.jpg", "UTF-8");
return ResponseEntity.ok()
// 内容类型
.contentType(MediaType.APPLICATION_OCTET_STREAM)
// 内容大小
.contentLength(bytes.length)
// Content-Disposition:内容处理方式,attachment:附件,filename:文件名
.header("Content-Disposition", "attachment;filename=" + encode)
.body(bytes);
}

// 模板代码
@RequestMapping("/download1")
public ResponseEntity<InputStreamResource> download1() throws IOException {
FileInputStream inputStream = new FileInputStream("D:\\Aimg\\MaYi.jpg");
// 1.文件名中文会乱码
// 2.文件太大会导致 内存溢出
// 解决:
// 1.URLEncoder.encode(filename,"UTF-8"):解决中文乱码问题
// 2.分片下载:解决内存溢出问题

// 1.中文乱码解决
String encode = URLEncoder.encode("美女.jpg", "UTF-8");
// 2.文件太大会oom,oom:内存溢出
// InputStreamResource:包装流,包装一个输入流,可以解决内存溢出问题

InputStreamResource resource = new InputStreamResource(inputStream);

return ResponseEntity.ok()
// 内容类型
.contentType(MediaType.APPLICATION_OCTET_STREAM)
// 内容大小
.contentLength(inputStream.available())
// Content-Disposition:内容处理方式,attachment:附件,filename:文件名
.header("Content-Disposition", "attachment;filename=" + encode)
.body(resource);
}
}