上传文件接口设计,SpringBoot + MinIO/S3 文件服务实现:FileService 接口与 FileServiceImpl 详解

在企业项目中,文件上传和管理是非常常见的需求。本文基于 芋道源码 的实现,介绍如何封装一个通用的 文件服务 FileService,支持:

  • 文件上传(保存数据库记录 + 存储文件到 S3/MinIO 等对象存储)

  • 文件下载与删除

  • 文件分页查询

  • 生成预签名上传地址(适合前端直传场景)


一、FileService 接口设计

首先定义 统一的文件服务接口,抽象业务逻辑,便于后续扩展。

public interface FileService {/*** 获得文件分页*/PageResult<FileDO> getFilePage(FilePageReqVO pageReqVO);/*** 保存文件,并返回访问路径*/String createFile(@NotEmpty byte[] content, String name, String directory, String type, String module);/*** 生成预签名地址信息(前端直传场景)*/FilePresignedUrlRespVO getFilePresignedUrl(@NotEmpty String name, String directory);/*** 数据库中保存文件*/Long createFile(FileCreateReqVO createReqVO);/*** 删除文件*/void deleteFile(Long id) throws Exception;/*** 获得文件内容(下载)*/byte[] getFileContent(Long configId, String path) throws Exception;
}

接口设计要点

  1. 上传文件:支持传入字节数组,自动处理文件名、路径、MIME 类型等。

  2. 预签名地址:便于前端直传文件,提升性能。

  3. 删除文件:既删除存储器里的物理文件,也删除数据库记录。

  4. 查询分页:方便后台管理。


二、FileServiceImpl 实现类

@Service
public class FileServiceImpl implements FileService {// 控制路径是否带日期/时间戳static boolean PATH_PREFIX_DATE_ENABLE = true;static boolean PATH_SUFFIX_TIMESTAMP_ENABLE = true;@Resourceprivate FileConfigService fileConfigService;@Resourceprivate FileMapper fileMapper;@Overridepublic PageResult<FileDO> getFilePage(FilePageReqVO pageReqVO) {if ("all".equals(pageReqVO.getModule())) {pageReqVO.setModule(null);}return fileMapper.selectPage(pageReqVO);}@Override@SneakyThrowspublic String createFile(byte[] content, String name, String directory, String type, String module) {// 1. 补全文件信息if (StrUtil.isEmpty(type)) {type = FileTypeUtils.getMineType(content, name);}if (StrUtil.isEmpty(name)) {name = DigestUtil.sha256Hex(content);}if (StrUtil.isEmpty(FileUtil.extName(name))) {String extension = FileTypeUtils.getExtension(type);if (StrUtil.isNotEmpty(extension)) {name = name + extension;}}// 2. 生成上传路径String path = generateUploadPath(name, directory);// 3. 上传文件FileClient client = fileConfigService.getMasterFileClient();Assert.notNull(client, "客户端(master) 不能为空");String url = client.upload(content, path, type);// 4. 保存数据库fileMapper.insert(new FileDO().setConfigId(client.getId()).setName(name).setPath(path).setUrl(url).setModule(module).setType(type).setSize(content.length));return url;}@VisibleForTestingString generateUploadPath(String name, String directory) {String prefix = PATH_PREFIX_DATE_ENABLE? LocalDateTimeUtil.format(LocalDateTimeUtil.now(), PURE_DATE_PATTERN): null;String suffix = PATH_SUFFIX_TIMESTAMP_ENABLE ? String.valueOf(System.currentTimeMillis()) : null;if (StrUtil.isNotEmpty(suffix)) {String ext = FileUtil.extName(name);if (StrUtil.isNotEmpty(ext)) {name = FileUtil.mainName(name) + "_" + suffix + "." + ext;} else {name = name + "_" + suffix;}}if (StrUtil.isNotEmpty(prefix)) {name = prefix + "/" + name;}if (StrUtil.isNotEmpty(directory)) {name = directory + "/" + name;}return name;}@Override@SneakyThrowspublic FilePresignedUrlRespVO getFilePresignedUrl(String name, String directory) {String path = generateUploadPath(name, directory);FileClient client = fileConfigService.getMasterFileClient();FilePresignedUrlRespDTO dto = client.getPresignedObjectUrl(path);return BeanUtils.toBean(dto, FilePresignedUrlRespVO.class,object -> object.setConfigId(client.getId()).setPath(path));}@Overridepublic Long createFile(FileCreateReqVO createReqVO) {FileDO file = BeanUtils.toBean(createReqVO, FileDO.class);fileMapper.insert(file);return file.getId();}@Overridepublic void deleteFile(Long id) throws Exception {FileDO file = validateFileExists(id);FileClient client = fileConfigService.getFileClient(file.getConfigId());Assert.notNull(client, "客户端({}) 不能为空", file.getConfigId());client.delete(file.getPath());fileMapper.deleteById(id);}private FileDO validateFileExists(Long id) {FileDO file = fileMapper.selectById(id);if (file == null) {throw exception(FILE_NOT_EXISTS);}return file;}@Overridepublic byte[] getFileContent(Long configId, String path) throws Exception {FileClient client = fileConfigService.getFileClient(configId);Assert.notNull(client, "客户端({}) 不能为空", configId);return client.getContent(path);}
}

三、核心逻辑拆解

1. 文件路径生成策略

  • 是否按日期分目录:20250908/xxx.png

  • 是否加时间戳后缀:避免文件名冲突

  • 可扩展为 UUID、Hash 等

2. 文件存储

  • 通过 FileClient 统一抽象,可以对接 MinIO、阿里云 OSS、七牛云、AWS S3 等。

  • 上传成功后,数据库保存一条记录(包括 url、路径、大小、类型、模块分类等)。

3. 文件删除

  • 先校验数据库记录是否存在

  • 调用存储器客户端删除物理文件

  • 再删除数据库记录

4. 文件预签名 URL

  • 用于前端直传文件到对象存储,避免文件先经过后端。

  • 返回结构中包含:uploadUrldownloadUrlpathconfigId 等信息。


四、应用场景

  • 后台管理系统:上传/下载合同、报表、Excel

  • 移动端 App:上传头像、图片、视频

  • 大文件上传:通过预签名直传,提升性能

  • 多存储支持:可按业务模块选择不同的存储配置


五、总结

通过 FileService + FileServiceImpl 的设计,我们实现了:

✅ 文件上传、下载、删除
✅ 文件路径唯一性控制
✅ 文件分页查询
✅ 预签名直传(提升性能)
✅ 存储器解耦(支持多云对象存储)

这种实现方式已经是 企业级最佳实践,可以非常方便地扩展到不同的云存储平台。

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

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

相关文章

MVC 依赖注入(DI)与服务全解析(附避坑实战)

依赖注入的核心概念 依赖注入&#xff08;DI&#xff09;是一种设计模式&#xff0c;通过将对象的依赖关系从内部创建转移到外部传递&#xff0c;实现解耦。在 MVC 框架中&#xff0c;DI 容器负责管理对象的生命周期和依赖关系&#xff0c;开发者只需声明依赖&#xff0c;容器…

【实证分析】上市公司经营风险数据集-含代码(2000-2022年)

数据简介&#xff1a;上市公司经营风险涉及多维度、多层次的复杂因素&#xff0c;本文章参考王竹泉-经营风险与营运资金融资决策对上市公司经验风险进行测算&#xff0c;经营风险是该公司息税折旧摊销前利润率的标准差&#xff0c;经营风险是该公司息税折旧摊销前利润率的标准差…

领码方案|Windows 下 PLT → PDF 转换服务超级完整版:异步、权限、进度

摘要 面向 Windows 平台&#xff0c;使用 ASP.NET Core Web API 结合 Ghostscript.NET 库&#xff0c;实现 PLT&#xff08;HPGL&#xff09;→PDF 的纯库调用转换&#xff0c;无需外部进程。支持同步与异步模式&#xff0c;采用 JWTRBAC 进行权限治理&#xff0c;任务状态存储…

浏览器兼容性问题全解:CSS 前缀、Grid/Flex 布局兼容方案与跨浏览器调试技巧

1. 浏览器兼容性与前缀问题 不同浏览器&#xff08;尤其是老版本 IE、Edge、Safari&#xff09;对新特性&#xff08;比如 CSS 变量、Grid、Flex 等&#xff09;的支持程度不一&#xff0c;需要使用厂商前缀&#xff08;-webkit-、-moz- 等&#xff09;或降级方案。新手往往忽…

【Android View】事件分发机制

参考文献 https://juejin.cn/post/6844904041487532045https://juejin.cn/post/6844903894103883789#heading-12https://www.jianshu.com/p/dea72779a6b7 文章目录

【大数据相关】ClickHouse命令行与SQL语法详解

ClickHouse命令行与SQL语法详解一、ClickHouse命令行与SQL语法详解第一部分&#xff1a;ClickHouse SQL 命令行客户端 (clickhouse-client)1. 基础连接2. 核心命令行参数3. 数据导入与导出实战第二部分&#xff1a;ClickHouse SQL 语法详解1. DDL (数据定义语言)2. DML (数据操…

学习日记-CSS-day53-9.11

1.CSS介绍知识点核心内容重点CSS定义层叠样式表&#xff0c;用于内容修饰和样式展现英文全称cascading style sheetsCSS作用实现HTML内容与样式分离&#xff0c;提高开发效率对比传统HTML元素单独设置样式的低效方式学习建议掌握常用功能即可&#xff0c;重点在打通前后端数据通…

Maven中optional的作用

目的&#xff1a; 控制依赖传递 &#xff1a;将依赖标记为可选&#xff0c;这样当其他模块依赖common-component时&#xff0c;不会自动继承Elasticsearch依赖。这遵循了"依赖最小化"原则&#xff0c;避免不必要的库被引入到不需要它们的模块中。模块化设计 &#xf…

蓝桥杯算法之基础知识(7)---排序题的快排和归并排序

一、快排》快排方法&#xff0c;就三步1.随便选一个值作为基准值x2.拿选中的这个x值划分队列为左右两个区间&#xff08;左边的都小于x&#xff0c;右边的都大于x&#xff09;3.然后递归左区间和右区间就行》代码举例&#xff1a;#qs排序#1 6 7 8 6 5 4 #先找比较点&#xff0c…

缓存未命中

缓存未命中&#xff08;Cache Miss&#xff09; 发生在 CPU 访问某块内存时&#xff0c;该地址不在当前缓存&#xff08;L1/L2/L3&#xff09;中&#xff0c;导致程序被迫从更慢的内存&#xff08;RAM&#xff09;读取数据&#xff0c;严重拖慢程序执行速度。 &#x1f4cd; 一…

AR眼镜:化工安全生产的技术革命

在石化企业的压缩机组巡检中&#xff0c;佩戴AR眼镜的巡检员眼前实时显示着设备温度场分布和振动频谱曲线&#xff0c;单台设备巡检时间从45分钟缩短至18分钟。这不仅是效率的提升&#xff0c;更是化工安全生产的一场智能革命。一、行业痛点&#xff1a;传统化工巡检的困境与挑…

消息中间件RabbitMQ(从入门到精通)

RabbitMQ概念_MQ 消息队列 MQ全称Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于系统之间的异步通信。 同步通信相当于两个人当面对话,你一言我一语。必须及时回复 异步通信相当于通过第三方转述对话,可能有消息的延迟,但不需要二人时刻保持联系。…

前端学习之后端java小白(五)之多表查询/事务

一、多表查询概念二、概述 1. 内连接隐式内连接 SELECT 字段列表 FROM 表1&#xff0c;表2... WHERE 条件显示内连接SELECT 字段列表 FROM 表1 [INNER] JOIN 表2 ON 条件2. 外连接 左外连接SELECT 列名 FROM 左表 LEFT [OUTER] JOIN 右表 ON 连接条件;右外连接SELECT 列名…

Java全栈学习笔记34

# JDBCjava database connection Java 数据库连接技术## JDBC 驱动程序如果需要通过jdbc技术连接关系型数据库&#xff0c;就需要为jdbc提供一个该数据库的驱动。驱动程序由对应的数据库厂商提供。mysql提供了针对于各种语言的驱动程序。去官网下载和java相关的驱动即可## JDB…

如何为MySQL中的JSON字段设置索引

背景 MySQL在2015年中发布的5.7.8版本中首次引入了JSON数据类型。自此&#xff0c;它成了一种逃离严格列定义的方式&#xff0c;可以存储各种形状和大小的JSON文档&#xff0c;例如审计日志、配置信息、第三方数据包、用户自定义字段等。 虽然MySQL提供了读写JSON数据的函数&am…

【学习日记】

1.上午看了会面经&#xff0c;八股&#xff0c;很多看不懂1.5排查本地mysql服务启动问题2.刷了两道题翻转二叉树的Dfs和bfs递归方法&#xff0c;看了几分钟看懂了&#xff0c;一开始刷题&#xff0c;没有这种感觉&#xff0c;可能思维上升了3.下午做了会ppt4.看了ssm的一个gith…

本地大模型部署指南-Ollama与HuggingFace对比

在本地部署大模型时&#xff0c;用 Ollama 和 Hugging Face (HF) 确实有很大区别&#xff0c;涉及系统、硬件、训练、推理方式&#xff0c;以及能否查看模型源代码。下面我分几个维度说明&#xff1a; 系统和安装 Ollama 定位是「开箱即用」的本地大模型运行环境。 自带运行时&…

河北周边有哪些比较靠谱的智算中心?

河北省通过算力普惠、绿色能源、数据开放、金融支持四大支柱政策&#xff0c;推动智算中心高质量发展。河北及周边地区的智算中心已形成高可靠性、先进技术和战略协同的布局。那么&#xff0c;河北周边有哪些比较靠谱的智算中心&#xff1f;一、河北周边智算中心盘点‍1、尚航怀…

电动汽车充电标准之 — 国标 GB/T 18487《电动汽车传导充电系统》 简介

GB/T 18487 的全称是 《电动汽车传导充电系统》 &#xff0c;它是中国电动汽车充电领域最基础、最核心的国家标准之一。该标准规定了电动汽车传导充电系统的通用要求、通信协议、安全要求等&#xff0c;是整个中国充电基础设施建设的基石。 与您之前了解的IEC 61851类似&#x…

温湿度传感器如何守护工业制造?

在工业制造、农业养殖、仓储物流乃至文物保护等领域&#xff0c;环境温湿度的精确监测是保障品质与安全的关键。温湿度传感器作为无声的守护者&#xff0c;如何通过稳定可靠的数据采集&#xff0c;为现代工业生产的精细化与智能化管理提供坚实基础&#xff1f;本文将深入探讨其…