从头开始搭建一个SpringBoot项目--SpringBoot文件的上传与下载
从头开始搭建一个SpringBoot项目--SpringBoot文件的上传
- 前言
- 流程分析
- 代码
- 结构
- 代码详情
- UploadFileInfo.class
- UploadController.class
- UploadDao.class
- UploadDao.xml
- UploadServices.class
- UploadServicesImpl.class
- 测试
- 下载
- 示例
前言
文件的上传和下载是很多系统必备的功能,之前的一篇文章简单描述了下载,那么现在我们来实现上传文件并且存储到服务器。
值得注意的是,在以下的示例系统中,我已经引入了SpringSecurity、Swagger、Mybatis等框架了。
详情参考: 从头开始搭建一个SpringBoot项目–SpringSecurity的配置
流程分析
用户上传文件,第一步肯定是选择文件,然后系统接收文件并保存到服务器,这一步里面我们首先要做的应该是获取此次上传信息,比如上传人、上传时间、上传文件类型等等。剩下的就是需要考虑的问题是,如何保存?
我的意思是如何保存这个文件?比如文件名称,如果有两个用户上传了一个同样名字的文件,那我们就让用户保存吗?那肯定是不可以的。所以保存时候很重要的一点就是,文件不能同名。所以这里我们保存在服务器上的文件名称最好做一个映射:用户定义文件名 -- 服务器保存文件名称。这种映射关系可以保存在数据库中,这样就保证了文件名的唯一性。
比如用户上传文件名称为你遭老罪了.png,保存的时候用一个时间戳或者uuid + 文件后缀作为该文件在服务器上的名称,假设为:123456789.png,然后把你遭老罪了--123456789,这样的一个映射关系保存到数据库里面,这样用户要按照文件名从服务器上获取该文件的时候我们也能的找到,保存的时候也不会有同名的风险。
PS: 同名风险还是有的,虽然很小,但确实存在:UUID可能生成相同,时间戳可能多用户上传时一样,如果需要的话保存前可以做一个文件名同名检查。如果文件名已存在,则再生成一个。
上传文件的流程图的话大致是以下这样:

代码
结构

代码详情
UploadFileInfo.class
@Setter
@Getter
@ToString
@NoArgsConstructor
public class UploadFileInfo {@ApiModelProperty(value = "上传人id")int uploaderId;@ApiModelProperty(value = "上传人名称")String uploaderName;@ApiModelProperty(value = "上传人时间")String uploadTime;@ApiModelProperty(value = "上传文件大小")int size;@ApiModelProperty(value = "上传文件名称")String fileName;@ApiModelProperty(value = "上传文件唯一名称")String uName;@ApiModelProperty(value = "存储路径")String storePath;@ApiModelProperty(value = "文件后缀")String extension;public UploadFileInfo(UserBean userBean) {if (userBean == null)throw new RuntimeException("用户为空");this.uploaderId = userBean.getId();this.uploaderName = userBean.getUsername();}
}
UploadController.class
这里面的日期工具类在我的其他文章里 主页搜索日期就可找到。
@RestController
@Controller
@RequestMapping(value = "/upload/")
@Api(tags = "03 上传文件" , position = 3)
public class UploadController {//配置文件中保存的位置@Value("${root.upload.path}")private String uploadPath;@AutowiredUploadServices uploadServices;@ApiOperation(value = "上传文件测试" , notes = "支持所有文件")@PostMapping(value = "/uploadTest")public Result uploadFile(@RequestParam(required = true) MultipartFile file) {UploadFileInfo up = getUploadInfo(file);System.out.println(up.toString());if(uploadServices.addUploadInfo(up) <= 0) {return ResultUtil.success(ResultCode.ERROR);}try {//将当前文件保存到服务器指定目录下的文件file.transferTo(new File(up.getStorePath()));}catch (Exception e) {e.printStackTrace();}return ResultUtil.success(ResultCode.SUCCESS);}/*** @Description* @Param file* @Return {@link UploadFileInfo}* @Author 三文鱼先生* @Date 2023/3/2 10:45**/public UploadFileInfo getUploadInfo(MultipartFile file) {UserBean user = (UserBean) SecurityContextHolder.getContext().getAuthentication().getPrincipal();//设置上上传人基本信息UploadFileInfo uploadFileInfo = new UploadFileInfo(user);//文件存储时的唯一idString uName = UUID.randomUUID().toString().replaceAll("-" , "");//当前时间uploadFileInfo.setUploadTime(DateUtil.formatStr(new Date() , DateUtil.SecondPattern));//文件大小uploadFileInfo.setSize((int) file.getSize());String[] fileName = file.getOriginalFilename().split("\\.");String extension = fileName[1];//存储路径String storePath = uploadPath + uName + "." + extension;//上传文件后缀uploadFileInfo.setExtension(extension);//服务器存储地址uploadFileInfo.setStorePath(storePath);//使用uuid作为文件在服务器上的名称uploadFileInfo.setUName(uName);//文件的真实名称uploadFileInfo.setFileName(fileName[0]);return uploadFileInfo;}}
UploadDao.class
public interface UploadDao {//添加上传文件记录信息int addUploadInfo(UploadFileInfo uploadFileInfo);
}
UploadDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.demo.uploadfile.dao.UploadDao"><insert id="addUploadInfo" parameterType="com.demo.uploadfile.bean.UploadFileInfo">insert into my_file(uploaderId,uploaderName,uploadTime,size,fileName,uName,storePath,extension) values(#{uploaderId},#{uploaderName},#{uploadTime},#{size},#{fileName},#{uName},#{storePath},#{extension});</insert>
</mapper>
UploadServices.class
public interface UploadServices {int addUploadInfo(UploadFileInfo uploadFileInfo);
}
UploadServicesImpl.class
@Service
public class UploadServicesImpl implements UploadServices {@AutowiredUploadDao uploadDao;@Overridepublic int addUploadInfo(UploadFileInfo uploadFileInfo) {return uploadDao.addUploadInfo(uploadFileInfo);}
}
测试


下载
下载的话就比较简单了,这里在之前的文章: 记SpringBoot下载的两种方式,在这里需要注意的应该是,配置SpringSecurity配置下不拦截/file/**。

示例
图片文件可以查看后 右键自定义保存

其他文件诸如:doc、excel、rar等都会直接下载。

相关文章:
从头开始搭建一个SpringBoot项目--SpringBoot文件的上传与下载
从头开始搭建一个SpringBoot项目--SpringBoot文件的上传前言流程分析代码结构代码详情UploadFileInfo.classUploadController.classUploadDao.classUploadDao.xmlUploadServices.classUploadServicesImpl.class测试下载示例前言 文件的上传和下载是很多系统必备的功能…...
It做形式主语和宾语
主谓宾,主宾能被名词性的sth,替换,如动名词,不定式,从句等等 而且,不能出现前面或者中间,很长,一大推的在开头或者中间,就产生了it做形式主宾。 一、It用作形式主语当不…...
做测试一定要知道的——软件测试流程和测试规范标准文档
目录 1、目的 2、工作范围 3、工作职责 4、测试的流程 5、测试准备阶段 6、测试方法制定阶段 7、测试执行阶段 8、bug管理 9、标准文档 总结感谢每一个认真阅读我文章的人!!! 重点:配套学习资料和视频教学 1、目的 通…...
Linux下将一个文件压缩分包成多个小文件
压缩分包 将文件test分包压缩成1G 的文件: tar czf - 文件名字 | split -b 10 - 文件名.tar.gz解压 将第一步分拆的多个包解压: cat 文件名.tar.gz* | tar -xzv...
分享5款用了一段时间,个人觉得非常nice的软件
大家在使用Windows办公、学习的时候,有没有觉得自己的电脑差了点意思?比如:电脑桌面上太杂乱、装满了各类五花八门的软件、桌面壁纸不美观等。今天,给大家分享五款个人用了段时间后,觉得非常nice的软件。 1.鼠标可视化…...
搜广推 Product-based Neural Networks (PNN) - 改进特征交叉的方式
😄 PNN:2016年上海交通大学提出。 文章目录 1、PNN1.1、原理1.2、创新点:product层1.3、product层z部分的输出:l~z~ 的计算方式:1.4、product层z部分的输出:l~p~ 的计算方式:1.4.1、IPNN1.4.2、OPNN1.5、优点1.6、缺点Reference1、PNN PNN:Product-based Neural Netwo…...
IDEA2022 配置spark开发环境
本人强烈建议在 linux环境下 学习 spark!!! Introduction Apache Spark是一个快速且通用的分布式计算引擎,可以在大规模数据集上进行高效的数据处理,包括数据转换、数据清洗、机器学习等。在本文中,我们将…...
趣味答题竞赛小程序开发功能的详细介绍
随着人们对知识学习的要求越来越高,答题已经成为了一项重要的学习和考核方式。而为了让答题变得更加有趣和富有挑战性,我们推出了趣味答题竞赛小程序。下面,我们将详细介绍这个小程序的开发功能。 1.个人淘汰赛 在个人淘汰赛中,…...
【独家】华为OD机试提供C语言题解 - 获取最大软件版本号
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 最近更新的博客使用说明获取…...
k8s编程operator实战之云编码平台——⑤项目完成、部署
文章目录1、效果展示2、保存用户状态和访问用户服务实现方案2.1 如何保存用户的状态2.1.1 解决保留安装的插件问题2.2 如何访问到用户在工作空间中启动的http服务2.2.1 code-server如何帮我们实现了用户程序的代理3、Operator功能实现3.1 使用KubeBuilder创建项目3.1.1 完善kin…...
C语言杂记(指针篇)
指针篇 指针就是地址,地址就是指针 指针变量就是存放地址的变量 *号只有定义的时候表示定义指针变量,其他表示从地址里面取内容 通过指针的方法使main函数中的data1和data2发生数据交换。 #include <stdio.h> void chang_data(int *data1,int *da…...
ES window 系统环境下连接问题
环境问题:(我采用的版本是 elasticsearch-7.9.3)注意 开始修正之前的配置:前提:elasticsearch.yml增加或者修正一下配置:xpack.security.enabled: truexpack.license.self_generated.type: basicxpack.secu…...
hexo部署github搭建个人博客 完整详细带图版(更新中)
文章目录0. 前置内容1. hexo创建个人博客2. GitHub创建仓库3. hexo部署到GitHub4. 常用命令newcleangenerateserverdeploy5. 添加插件5.1 主题5.2 博客基本信息5.3 创建新的菜单5.4 添加搜索功能5.5 添加阅读时间字数提示5.6 打赏功能5.7 切换主题5.8 添加不蒜子统计5.9 添加百…...
SpringBoot集成DruidDataSource实现监控 SQL 性能
一、快速入门 1.1 基本概念 我们都使用过连接池,比如C3P0、DBCP、hikari、Druid,虽然 HikariCP 的速度稍快,但 Druid 能够提供强大的监控和扩展功能。Druid DataSource 是阿里巴巴开发的号称为监控而生的数据库连接池,它不仅可以…...
maven镜像源及代理配置
在公司使用网络一般需要设置代理, 我在idea中创建springboot工程时,发现依赖下载不了,原以为只要浏览器设置代理,其他的网络访问都会走代理,经过查资料设置了以下几个地方后工程创建正常,在此记录给大家参考…...
【Java面试篇】Spring中@Transactional注解事务失效的常见场景
文章目录Transactional注解的失效场景☁️前言🍀前置知识🍁场景一:Transactional应用在非 public 修饰的方法上🍁场景二: propagation 属性设置错误🍁场景三:rollbackFor属性设置错误dz…...
【C】分配内存的函数
#include <stdlib.h>//分配所需的内存空间,并返回一个指向它的指针。 void *malloc(size_t size);//分配所需的内存空间,并返回一个指向它的指针。并且calloc负责把这块内存空间用字节0填//充,而malloc并不负责把分配的内存空间清零 vo…...
IDEA 断点总是进入class文件没有进入源文件解决
前言 idea 断点总是进入class文件没有进入源文件解决 问题 在源文件里打了断点,断点模式启动时却进入了class文件里的断点,而没有进入到java源文件里的断点。 比如:我在 A.java 里打了断点,调试时却进入到了 jar 包里的 A.clas…...
【flink】 flink入门教程demo 初识flink
文章目录通俗解释什么是flink及其应用场景flink处理流程及核心APIflink代码快速入门flink重要概念什么是flink? 刚接触这个词的同学 可能会觉得比较难懂,网上搜教程 也是一套一套的官话, 如果大家熟悉stream流,那或许会比较好理解…...
LeetCode 1487. 保证文件名唯一
【LetMeFly】1487.保证文件名唯一 力扣题目链接:https://leetcode.cn/problems/making-file-names-unique/ 给你一个长度为 n 的字符串数组 names 。你将会在文件系统中创建 n 个文件夹:在第 i 分钟,新建名为 names[i] 的文件夹。 由于两个…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
