从零开始实现文件的上传下载功能,如果不清楚怎么搭建springboot3工程,可以看这篇笔记十分钟快速启一个后端接口,看到新建controller就可以。然后跟着下面的步骤,一步一步来就可以实现。
1. 先安装一个hutool工具包,方便处理数据
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
</dependency>
2.新建FileController
1.1 文件上传接口
关键代码如下:都添加了注释,每一个步骤都解释的很清楚。
@RestController
@RequestMapping("/files")
public class FileController {
// 4. 使用系统属性获取当前工作目录(项目根路径),并拼接"/files/"作为文件存储目录
// 系统属性"user.dir"返回JVM启动路径(通常是项目根目录)
private static final String filePath = System.getProperty("user.dir") + "/files/";
// 文件上传的接口
@PostMapping("/upload")
public Result upload(MultipartFile file) {// // 1. 接收前端以multipart/form-data格式提交的文件
String originalFilename = file.getOriginalFilename();//. 2.获取原始文件名(含扩展名,如"test.png")
// 3. 检查目标目录是否存在(FileUtil.isDirectory判断路径是否为目录)
if (!FileUtil.isDirectory(filePath)) {
FileUtil.mkdir(filePath);
}
// 5. 生成唯一文件名:时间戳 + 原始文件名(避免文件名冲突)
String fileName = System.currentTimeMillis() + "_" + originalFilename;
// 6. 拼接文件在服务器上的绝对路径
String realPath = filePath + fileName;
try {
// 7. 将文件内容写入磁盘(FileUtil.writeBytes封装了文件写入操作)
FileUtil.writeBytes(file.getBytes(), realPath);
// 参数1: file.getBytes() 获取文件字节数组
// 参数2: realPath 文件存储的完整物理路径
} catch (IOException e) {
throw new CustomException("500", "文件上传失败");
}
// 8. 构建文件访问URL(假设服务可通过/files/download路径提供文件下载)
String url= "http://localhost:9090/files/download"+fileName;
return Result.success(url);
}
}
ps: 代码中的错误处理函数我放在文章最后面
关键代码解析:
-
System.getProperty("user.dir")
- 功能:获取JVM启动的工作目录(通常是项目根目录)
- 示例输出:
/home/projects/myapp
-
FileUtil.isDirectory()
(来自hutool工具包)- 功能:检查指定路径是否为已存在的目录
- 返回:
true
(是目录且存在) /false
(不存在或不是目录)
-
FileUtil.mkdir()
(来自hutool工具包)- 功能:递归创建目录(自动创建父目录)
- 示例:
mkdir("/a/b/c")
会连续创建a、b、c三级目录
-
MultipartFile.getOriginalFilename()
- 功能:获取客户端上传文件的原始文件名(包含扩展名)
-
FileUtil.writeBytes()
(来自hutool工具包)- 功能:将字节数组写入指定路径
- 等效于Java原生:
Files.write(Paths.get(realPath), bytes)
-
MultipartFile.getBytes()
- 功能:将上传文件内容转换为字节数组
对接口进行验证,使用apipost进行接口测试
1.2 文件下载接口
// 文件下载接口(GET请求)
@GetMapping("/download/{fileName}")
public void download(@PathVariable String fileName, HttpServletResponse response) {
try {
// 1. 设置响应内容类型为二进制流(通用文件下载格式)
// 浏览器会将其识别为需要下载的文件,而不是直接打开
response.setContentType("application/octet-stream");
// 2. 设置Content-Disposition响应头,控制浏览器下载行为
// "attachment"表示作为附件下载(而不是内联显示)
// filename指定下载时保存的默认文件名(使用URL编码解决中文乱码问题)
response.addHeader("Content-Disposition", "attachment; filename=" +
URLEncoder.encode(fileName, StandardCharsets.UTF_8));
// 3. 获取响应输出流(用于向客户端发送文件数据)
OutputStream os = response.getOutputStream();
// 4. 拼接文件在服务器上的完整物理路径
String realPath = filePath + fileName;
// 5. 读取文件内容到字节数组(FileUtil.readBytes来自hutool工具包)
byte[] bytes = FileUtil.readBytes(realPath);
// 6. 将文件字节流写入响应输出流
os.write(bytes);
// 7. 刷新输出流确保数据完全发送
os.flush();
// 8. 关闭输出流释放资源
os.close();
} catch (IOException e) {
// 9. 异常处理:文件操作出错时抛出业务异常
throw new CustomException("500", "文件下载失败");
}
}
关键代码解析:
-
@GetMapping("/download/{fileName}")
- 定义GET请求端点,
{fileName}
为路径参数 - 示例访问路径:
http://localhost:9090/files/download/1686543211234_logo.png
- 定义GET请求端点,
-
@PathVariable String fileName
- 从URL路径中提取文件名参数
- 如访问
/download/test.jpg
→fileName="test.jpg"
-
HttpServletResponse
- 用于直接操作HTTP响应(设置头信息/输出流)
- 相比返回对象,提供更底层的响应控制
-
response.setContentType("application/octet-stream")
- MIME类型设置为二进制流
- 强制浏览器触发下载行为而非直接打开文件
-
Content-Disposition
响应头attachment
:指示浏览器应保存文件filename
:指定下载时的默认文件名URLEncoder.encode()
:解决中文文件名乱码问题
对接口进行验证,浏览器输入地址即可