Spring Boot 实战:轻松实现文件上传与下载功能

news2025/3/18 20:59:22

目录

一、引言

二、Spring Boot 文件上传基础

(一)依赖引入

(二)配置文件设置

(三)文件上传接口编写

(一)文件类型限制

(二)文件大小验证

(三)防止文件覆盖

四、Spring Boot 文件下载实现

(一)简单文件下载接口编写

(二)文件下载的异常处理

(三)支持断点续传

五、实战案例演示

六、总结与展望


一、引言

在当今的 Web 应用开发中,文件上传与下载功能是极为常见且重要的需求。无论是用户上传头像、分享文档,还是系统生成报告供用户下载,都离不开这一功能模块。Spring Boot 作为一款流行的 Java 开发框架,为我们提供了简洁高效的方式来实现文件上传与下载。本文将详细介绍如何基于 Spring Boot 框架轻松搭建并实现这一功能,让你快速掌握其核心要点与实践技巧。

二、Spring Boot 文件上传基础

(一)依赖引入

在 Spring Boot 项目中,首先需要引入相关依赖。对于文件上传功能,除了基础的spring-boot-starter-web依赖外,还需要添加处理文件上传的commons-fileupload依赖。在pom.xml文件中添加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

spring-boot-starter-web提供了构建 Web 应用的基础功能,而commons-fileupload则专门用于处理文件上传操作。

(二)配置文件设置

application.properties配置文件中设置与文件上传相关的参数。例如:

?
# 设置单个文件上传的最大大小为 10MB
spring.servlet.multipart.max-file-size=10MB
# 设置一次请求中上传文件的总大小为 20MB
spring.servlet.multipart.max-request-size=20MB
# 设置上传文件的临时目录
spring.servlet.multipart.location=/tmp/uploads

?

这里分别设置了单个文件大小限制、总请求文件大小限制以及上传文件的临时存储目录。这些配置可以根据实际项目需求进行调整。

(三)文件上传接口编写

编写一个简单的文件上传接口,接收前端传来的文件数据。创建一个FileUploadController类:

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

@RestController
public class FileUploadController {

    @PostMapping("/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return "上传文件为空,请选择文件后再次上传。";
        }
        try {
            // 获取文件名
            String fileName = file.getOriginalFilename();
            // 获取文件存储路径,这里假设存储在项目根目录下的 uploads 文件夹中
            String filePath = System.getProperty("user.dir") + "/uploads/" + fileName;
            // 将文件保存到指定路径
            file.transferTo(new File(filePath));
            return "文件上传成功,文件路径:" + filePath;
        } catch (IOException e) {
            e.printStackTrace();
            return "文件上传失败:" + e.getMessage();
        }
    }
}

在上述代码中,@RestController表示这是一个处理 RESTful 风格请求的控制器类。@PostMapping("/upload")注解指定了该方法处理POST请求到/upload路径的逻辑。@RequestParam("file") MultipartFile file用于接收前端传来的名为file的文件数据。通过file.isEmpty()判断文件是否为空,如果不为空,则获取文件的原始名称getOriginalFilename(),构建文件存储路径,最后使用transferTo()方法将文件保存到指定路径。如果保存过程中出现IOException异常,则打印异常信息并返回错误提示。

(一)文件类型限制

可以通过白名单的方式对上传文件的类型进行限制。例如,只允许上传图片文件(如.jpg.png.gif):

private static final String[] ALLOWED_FILE_TYPES = { "image/jpeg", "image/png", "image/gif" };

@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
    if (file.isEmpty()) {
        return "上传文件为空,请选择文件后再次上传。";
    }
    // 检查文件类型是否在允许列表中
    if (!Arrays.asList(ALLOWED_FILE_TYPES).contains(file.getContentType())) {
        return "不允许上传该类型的文件,请上传图片文件(jpg、png、gif)。";
    }
    try {
        // 后续文件保存逻辑...
    } catch (IOException e) {
        e.printStackTrace();
        return "文件上传失败:" + e.getMessage();
    }
}

上述代码中,定义了一个允许的文件类型数组ALLOWED_FILE_TYPES,然后在上传文件前检查文件的ContentType是否在允许列表中,如果不在,则返回错误提示。

(二)文件大小验证

除了配置文件中的全局限制,在代码层面也可以再次验证单个文件大小:

@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
    if (file.isEmpty()) {
        return "上传文件为空,请选择文件后再次上传。";
    }
    // 检查文件大小是否超过 5MB
    if (file.getSize() > 5 * 1024 * 1024) {
        return "上传文件过大,单个文件大小不能超过 5MB。";
    }
    // 后续文件类型检查及保存逻辑...
}

这里通过file.getSize()获取文件大小,并与设定的限制(5MB)进行比较,如果超过则返回错误提示。

(三)防止文件覆盖

采用时间戳生成唯一文件名来防止文件覆盖:

@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
    if (file.isEmpty()) {
        return "上传文件为空,请选择文件后再次上传。";
    }
    try {
        // 获取文件名
        String originalFileName = file.getOriginalFilename();
        // 获取文件后缀名
        String fileExtension = originalFileName.substring(originalFileName.lastIndexOf("."));
        // 生成唯一文件名,使用当前时间戳
        String uniqueFileName = System.currentTimeMillis() + fileExtension;
        // 获取文件存储路径,这里假设存储在项目根目录下的 uploads 文件夹中
        String filePath = System.getProperty("user.dir") + "/uploads/" + uniqueFileName;
        // 将文件保存到指定路径
        file.transferTo(new File(filePath));
        return "文件上传成功,文件路径:" + filePath;
    } catch (IOException e) {
        e.printStackTrace();
        return "文件上传失败:" + e.getMessage();
    }
}

通过获取原始文件名的后缀名,结合当前时间戳生成一个唯一的文件名,确保每次上传的文件都有独立的标识,避免覆盖同名文件。

四、Spring Boot 文件下载实现

(一)简单文件下载接口编写

创建一个文件下载接口,如下:

import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;

@RestController
public class FileDownloadController {

    @GetMapping("/download")
    public ResponseEntity<FileSystemResource> downloadFile(@RequestParam("fileName") String fileName) {
        // 获取文件路径,这里假设文件存储在项目根目录下的 uploads 文件夹中
        String filePath = System.getProperty("user.dir") + "/uploads/" + fileName;
        File file = new File(filePath);
        if (file.exists()) {
            // 设置响应头信息,包括文件名和文件类型
            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName);
            headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
            // 返回文件资源
            return ResponseEntity.ok()
                  .headers(headers)
                  .body(new FileSystemResource(file));
        } else {
            return ResponseEntity.notFound().build();
        }
    }
}

在这个代码中,@GetMapping("/download")表示处理GET请求到/download路径的逻辑。根据前端传入的文件名参数fileName,构建文件路径并检查文件是否存在。如果存在,则设置响应头信息,包括Content-Disposition用于指定文件名和下载方式(attachment表示下载),Content-Type设置为APPLICATION_OCTET_STREAM_VALUE表示通用的二进制流文件类型。最后通过ResponseEntity返回文件资源,若文件不存在则返回404 Not Found状态。

(二)文件下载的异常处理

在上述代码中,如果文件不存在则返回404状态。还可以进一步处理其他可能的异常,例如文件读取错误:

@GetMapping("/download")
public ResponseEntity<FileSystemResource> downloadFile(@RequestParam("fileName") String fileName) {
    String filePath = System.getProperty("user.dir") + "/uploads/" + fileName;
    File file;
    try {
        file = new File(filePath);
        if (file.exists()) {
            // 设置响应头信息...
            return ResponseEntity.ok()
                  .headers(headers)
                  .body(new FileSystemResource(file));
        } else {
            return ResponseEntity.notFound().build();
        }
    } catch (Exception e) {
        e.printStackTrace();
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
}

这里捕获了可能出现的异常,并在异常发生时返回500 Internal Server Error状态码,表示服务器内部错误。

(三)支持断点续传

对于大文件下载实现断点续传功能:

@GetMapping("/download")
public ResponseEntity<Resource> downloadFile(@RequestParam("fileName") String fileName,
                                              @RequestHeader(value = "Range", required = false) String rangeHeader) {
    String filePath = System.getProperty("user.dir") + "/uploads/" + fileName;
    File file = new File(filePath);
    if (file.exists()) {
        try {
            // 获取文件长度
            long fileLength = file.length();
            // 处理 Range 请求头
            HttpHeaders headers = new HttpHeaders();
            if (rangeHeader!= null && rangeHeader.startsWith("bytes=")) {
                long startRange = Long.parseLong(rangeHeader.substring("bytes=".length()).split("-")[0]);
                long endRange = fileLength - 1;
                if (rangeHeader.contains("-")) {
                    endRange = Long.parseLong(rangeHeader.substring("bytes=".length()).split("-")[1]);
                }
                // 设置响应头的 Content-Range 字段
                headers.add(HttpHeaders.CONTENT_RANGE, "bytes " + startRange + "-" + endRange + "/" + fileLength);
                headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(endRange - startRange + 1));
                headers.add(HttpHeaders.ACCEPT_RANGES, "bytes");
                // 设置响应状态码为 206 Partial Content
                return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
                      .headers(headers)
                      .body(new FileSystemResource(file).createRelative(startRange, endRange));
            } else {
                headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileLength));
                headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
                headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName);
                return ResponseEntity.ok()
                      .headers(headers)
                      .body(new FileSystemResource(file));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    } else {
        return ResponseEntity.notFound().build();
    }
}

在上述代码中,首先获取文件的总长度fileLength。然后检查请求头中的Range信息,如果存在Range请求,则解析出起始和结束位置startRangeendRange,设置响应头的Content-RangeContent-LengthACCEPT_RANGES字段,并返回206 Partial Content状态码,表示部分内容响应,同时通过createRelative()方法读取文件指定范围的数据返回给客户端。如果没有Range请求,则按照普通下载方式设置响应头并返回整个文件。

五、实战案例演示

通过一个完整的 Spring Boot 项目实例,演示文件上传与下载功能的实际应用。包括前端页面的设计与交互(使用 HTML、JavaScript 等前端技术实现简单的文件上传和下载按钮及相关提示信息),以及后端 Spring Boot 代码的具体实现细节。展示如何将文件上传与业务逻辑相结合,例如在用户注册时上传头像,并在用户个人资料页面实现头像的下载显示;或者在一个文档管理系统中,实现文件的上传、分类存储以及用户按需下载等功能场景。

六、总结与展望

总结本文所介绍的 Spring Boot 文件上传与下载功能的实现步骤、关键要点以及注意事项。强调在实际开发过程中,安全性与稳定性是至关重要的因素,需要开发者充分考虑各种边界情况并进行合理的处理。同时,展望未来可能的扩展方向,如与云存储服务集成,实现更强大、灵活的文件管理功能,以满足日益增长的业务需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2317421.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

并发编程面试题二

1、java线程常见的基本状态有哪些&#xff0c;这些状态分别是做什么的 &#xff08;1&#xff09;创建&#xff08;New&#xff09;&#xff1a;new Thread()&#xff0c;生成线程对象。 &#xff08;2&#xff09;就绪&#xff08;Runnable&#xff09;:当调用线程对象的sta…

Spring Cloud Stream - 构建高可靠消息驱动与事件溯源架构

一、引言 在分布式系统中&#xff0c;传统的 REST 调用模式往往导致耦合&#xff0c;难以满足高并发和异步解耦的需求。消息驱动架构&#xff08;EDA, Event-Driven Architecture&#xff09;通过异步通信、事件溯源等模式&#xff0c;提高了系统的扩展性与可观测性。 作为 S…

突破连接边界!O9201PM Wi-Fi 6 + 蓝牙 5.4 模块重新定义笔记本无线体验

在当今数字化时代&#xff0c;笔记本电脑已成为人们工作、学习和娱乐的必备工具。而无线连接技术&#xff0c;作为笔记本电脑与外界交互的关键桥梁&#xff0c;其性能的优劣直接关乎用户体验的好坏。当下&#xff0c;笔记本电脑无线连接领域存在诸多痛点&#xff0c;严重影响着…

Python----计算机视觉处理(Opencv:图像颜色替换)

一、开运算 开运算就是对图像先进行腐蚀操作&#xff0c; 然后进行膨胀操作。开运算可以去除二值化图中的小的噪点&#xff0c;并分离相连的物体。 其主要目的就是消除那些小白点 在开运算组件中&#xff0c;有一个叫做kernel的参数&#xff0c;指的是核的大小&#xff0c;通常…

一周学会Flask3 Python Web开发-SQLAlchemy查询所有数据操作-班级模块

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们来新建一个的蓝图模块-班级模块&#xff0c;后面可以和学生模块&#xff0c;实现一对多的数据库操作。 blueprint下新建g…

Matlab 风力发电机磁悬浮轴承模型pid控制

1、内容简介 略 Matlab 174-风力发电机磁悬浮轴承模型pid控制 可以交流、咨询、答疑 2、内容说明 磁悬浮轴承具有无接触、无摩擦、高速度、高精度、能耗低、不需要需润滑无油污染、可靠性高、寿命长和密封等一系列显著的优点。将磁悬浮技术应用于风力发电机中可以降低风机切入…

FPGA中级项目1——IP核(ROM 与 RAM)

FPGA中级项目1——IP核&#xff08;ROM 与 RAM&#xff09; IP核简介 在 FPGA&#xff08;现场可编程门阵列&#xff09;设计中&#xff0c;IP 核&#xff08;Intellectual Property Core&#xff0c;知识产权核&#xff09;是预先设计好的、可重用的电路模块&#xff0c;用于实…

Matlab 基于专家pid控制的时滞系统

1、内容简介 Matlab 185-基于专家pid控制的时滞系统 可以交流、咨询、答疑 2、内容说明 略 在处理时滞系统&#xff08;Time Delay Systems&#xff09;时&#xff0c;使用传统的PID控制可能会面临挑战&#xff0c;因为时滞会导致系统的不稳定或性能下降。专家PID控制通过结…

Unity 笔记:在EditorWindow中绘制 Sorting Layer

在Unity开发过程中&#xff0c;可能会对旧资源进行批量修改&#xff0c;一个个手动修改费人费事&#xff0c;所以催生出了一堆批量工具。 分享一下在此过程中绘制 Sorting Layer 面板的代码脚本。 示意图&#xff1a; 在 EditorGUI 和 EditorGUILayer 中内置了 SortingLayerF…

2024浙江大学计算机考研上机真题

2024浙江大学计算机考研上机真题 2024浙江大学计算机考研复试上机真题 2024浙江大学计算机考研机试真题 2024浙江大学计算机考研复试机试真题 历年浙江大学计算机复试上机真题 历年浙江大学计算机复试机试真题 2024浙江大学计算机复试上机真题 2024浙江大学计算机复试机试真题 …

蓝桥杯嵌入式赛道复习笔记2(按键控制LED灯,双击按键,单击按键,长按按键)

硬件原理解释 这张图展示了一个简单的按键电路原理图&#xff0c;其中包含四个按键&#xff08;PB0、PB1、PB2、PB3、PA0&#xff09;&#xff0c;每个按键通过一个10kΩ的上拉电阻连接到VDD&#xff08;电源电压&#xff09;&#xff0c;并接地&#xff08;GND&#xff09;。 …

每天五分钟深度学习PyTorch:循环神经网络RNN的计算以及维度信息

本文重点 前面我们学习了RNN从何而来,以及它的一些优点,我们也知道了它的模型的大概情况,本文我们将学习它的计算,我们来看一下RNN模型的每一个时间步在计算什么? RNN的计算 ht-1是上一时刻的输出,xt是本时刻的输入,然后二者共同计算得到了ht,然后yt通过ht计算得到,…

Ubuntu docker安装milvusdb

一、安装docker 1.更新软件包 sudo apt update sudo apt upgrade sudo apt-get install docker-ce docker-ce-cli containerd.io查看是否安装成功 docker -v二、使用国内的镜像下载 milvusdb Docker中国区官方镜像: https://registry.docker-cn.com milvusdb/milvus - Doc…

【优选算法篇】--深度解析之滑动窗口篇

滑动窗口 一、长度最小的子数组二、无重复字符的最长子串三、最大连续1的个数III四、水果成篮 一、长度最小的子数组 长度最小的子数组 解析&#xff1a; 首先看到这题 我们首先想到的是暴力枚举&#xff0c;就是暴力枚举所有子数组和。时间复杂度是O(n^3)。 我们这里用解法…

[STM32]新建工程||一个工程文件应该有哪些基本内容?

目录 一 、开发方法 1.直接使用程序来配置寄存器 2.基于库函数的方式 3.基于HAL库的方式 二 、常规的工程文件分类 STM32芯片型号分类以及缩写 ​三 步骤总结 四 工程架构 五 调用外设基本通用步骤 一 、开发方法 1.直接使用程序来配置寄存器 底层&#xff0c;直接&…

python编写的一个打砖块小游戏

游戏介绍 打砖块是一款经典的街机游戏&#xff0c;玩家控制底部的挡板&#xff0c;使球反弹以击碎上方的砖块。当球击中砖块时&#xff0c;砖块消失&#xff0c;球反弹&#xff1b;若球碰到挡板&#xff0c;则改变方向继续运动&#xff1b;若球掉出屏幕底部&#xff0c;玩家失…

【菜鸟飞】通过vsCode用python访问公网deepseek-r1等模型(Tocken模式)

目标 通过vsCode用python访问deepseek。 环境准备 没有环境的&#xff0c;vscode环境准备请参考之前的文章&#xff0c;另外需安装ollama&#xff1a; 【菜鸟飞】用vsCode搭建python运行环境-CSDN博客 AI入门1&#xff1a;AI模型管家婆ollama的安装和使用-CSDN博客 选读文章…

Figma介绍(基于云的协作式界面设计工具,主要用于UI/UX设计、原型制作和团队协作)

文章目录 注册和登录简单操作说明Figma介绍**核心特点**1. **云端协作与实时同步**2. **跨平台兼容**3. **高效设计工具**4. **原型交互与动效**5. **开发对接友好**6. **插件生态**7. **版本控制与历史记录** **适用场景**- **团队协作**&#xff1a;远程团队共同设计、评审、…

Text-to-SQL将自然语言转换为数据库查询语句

有关Text-To-SQL方法&#xff0c;可以查阅我的另一篇文章&#xff0c;Text-to-SQL方法研究 直接与数据库对话-text2sql Text2sql就是把文本转换为sql语言&#xff0c;这段时间公司有这方面的需求&#xff0c;调研了一下市面上text2sql的方法&#xff0c;比如阿里的Chat2DB,麻…

XSS漏洞靶场---(复现)

XSS漏洞靶场—&#xff08;复现&#xff09; 反射型 XSS 的特点是攻击者诱导用户点击包含恶意脚本的 URL&#xff0c;服务器接收到请求后将恶意脚本反射回响应页面&#xff0c;浏览器执行该脚本从而造成攻击&#xff0c;恶意脚本不会在服务器端存储。 Level 1(反射型XSS) 此漏…