当前位置: 首页 > news >正文

从零用java实现 小红书 springboot vue uniapp(15) 集成minio存储 支持本地和minio切换

从零用java实现 小红书 springboot vue uniapp(15) 集成minio存储 支持本地和minio切换

移动端演示 http://8.146.211.120:8081/#/

管理端演示 http://8.146.211.120:8088/#/

项目整体介绍及演示

前言

随着我们的“小红书”项目功能逐渐丰富,特别是加入了视频笔记功能后,文件存储成为了一个亟待优化的核心问题。将用户上传的图片、视频等文件直接存储在应用服务器的本地磁盘上,虽然在开发初期简单快捷,但很快会暴露出一系列问题:服务器磁盘空间有限、数据难以迁移和备份、并且不利于未来服务的水平扩展。

为了解决这些问题,我们需要引入对象存储服务。MinIO 是一个开源的高性能对象存储,它与 Amazon S3 API 兼容,可以作为私有云存储的绝佳选择。本文将详细介绍如何在我们的 Spring Boot 项目中集成 MinIO,并设计一个灵活的切换机制,让我们能够一键在本地存储MinIO存储之间切换,以适应不同的部署环境。


在这里插入图片描述
管理端
在这里插入图片描述

核心技术实现

我们的目标是让文件上传的业务逻辑与具体的存储方式解耦。无论文件最终存到哪里,对于上层调用者(Controller)来说,方法都是一样的。我们将通过配置、服务封装和策略模式的思想来实现这一目标。

1. 统一化配置:application.yml

配置是实现动态切换的第一步。我们在 application.yml 中定义所有与文件存储相关的参数,并增加一个关键的切换开关:storage-mode

dd:# 本地文件存储路径uploadPath: C:/ddStore/# 生产环境配置域名,如 example.com,本地开发环境留空domain:# 是否使用HTTPS,生产环境设为trueuse-https: false# 文件存储模式:local(本地存储) 或 minio(MinIO对象存储)storage-mode: local# MinIO配置
minio:# MinIO服务地址endpoint: http://192.168.10.105:9000# 访问密钥accessKey: admin# 秘密密钥secretKey: password# 存储桶名称bucketName: book
  • dd.storage-mode: 这是我们的核心开关。当值为 local 时,系统使用本地磁盘存储;当值为 minio 时,则使用 MinIO。
  • dd.uploadPath: local 模式下文件的存放根目录。
  • minio.*: 包含了连接 MinIO 服务所需的所有信息。
2. MinIO 核心服务:MinioService.java

我们首先创建一个 MinioService,这个类是与 MinIO 服务器直接交互的“专家”。它封装了所有底层的 MinIO API 调用,如检查桶是否存在、上传、删除和获取文件URL等。

MinioService.java:

@Slf4j
@Service
public class MinioService {@Autowiredprivate MinioClient minioClient;@Value("${minio.bucketName}")private String bucketName;/*** 上传文件** @param file 文件* @param objectName 在MinIO中存储的对象名称(路径+文件名)* @return 文件访问URL*/public String uploadFile(MultipartFile file, String objectName) {try {// 确保存储桶存在,不存在则自动创建if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}// 执行上传minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build());log.info("文件上传成功 (MinIO): {}", objectName);// 返回文件的预签名访问URL,有效期7天return getFileUrl(objectName);} catch (Exception e) {log.error("文件上传至MinIO失败: {}", e.getMessage());throw new RuntimeException("文件上传失败", e);}}/*** 获取文件访问URL (预签名URL)*/public String getFileUrl(String objectName) {try {return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectName).expiry(7, TimeUnit.DAYS) // 7天有效期.build());} catch (Exception e) {// ...}}// ... 其他方法如删除、下载等
}

这个服务将复杂的 MinIO SDK 调用包装成了简单的方法,供我们上层的文件服务(FileService)调用。

3. 策略切换核心:FileServiceImpl.java

这部分是实现存储模式切换的“大脑”。我们创建一个 FileService 接口和它的实现类 FileServiceImplFileServiceImpl 会根据 application.yml 中的 dd.storage-mode 配置来决定具体的文件存储策略。

FileServiceImpl.java:

@Service
public class FileServiceImpl implements FileService {@Value("${dd.storage-mode}")private String storageMode;@Value("${dd.uploadPath}")private String localUploadPath;@Autowired(required = false) // MinIO服务在local模式下可能不启用,允许不注入private MinioService minioService;@Overridepublic String upload(MultipartFile file, String bizPath) {String finalUrl;// 生成唯一的文件名String fileName = bizPath + "/" + UUID.randomUUID().toString().replace("-", "") + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));if ("minio".equals(storageMode)) {// MinIO 模式finalUrl = minioService.uploadFile(file, fileName);} else {// Local 模式File dest = new File(localUploadPath + fileName);if (!dest.getParentFile().exists()) {dest.getParentFile().mkdirs();}try {file.transferTo(dest);} catch (IOException e) {throw new RuntimeException("本地文件上传失败", e);}// 构造本地访问URL,需要配合静态资源映射finalUrl = "/upload/" + fileName; }return finalUrl;}
}

通过读取 storageMode 变量,upload 方法内部形成了一个 if-else 分支,从而实现了逻辑的动态分派。

4. 控制层调用:FileController.java

最后,我们的 Controller 层保持了极度的简洁。它只负责接收请求和调用 FileService,完全不知道底层是用本地方式还是 MinIO 方式存储。

FileController.java:

@RestController
@RequestMapping("/appUpload")
public class FileController {@Autowiredprivate FileService fileService;@PostMapping("/upload")@ApiOperation(value = "文件上传", notes = "通用文件上传接口")public ResultBean<String> upload(@RequestParam(value = "file") MultipartFile file,@RequestParam(value = "biz", required = false, defaultValue = "common") String biz) {// 直接调用服务层,无需关心具体实现String url = fileService.upload(file, biz);if (url != null && !url.isEmpty()) {return ResultBean.success("上传成功", url);}return ResultBean.error("上传失败");}
}

这种设计完美体现了“面向接口编程”和“单一职责”原则。Controller 的职责就是处理HTTP请求,存储的复杂性被完全封装在了服务层。

通过这种方式,我们不仅成功集成了 MinIO,还构建了一个高内聚、低耦合的文件存储系统。未来如果需要支持其他云存储(如阿里云OSS、七牛云Kodo),只需增加一个新的 else if 分支和对应的 Service 即可,对现有业务代码毫无影响。

我们可以根据存在业务表的图片id 关联文件表 然后返回相应的url

代码地址
https://gitee.com/ddeatrr/springboot_vue_xhs

http://www.dtcms.com/a/295976.html

相关文章:

  • Could not open JDBC Connection for transaction 问题
  • 基于SpringBoot+Uniapp的健身饮食小程序(协同过滤算法、地图组件)
  • 卫星物联网:使用兼容 Arduino 的全新 Iridium Certus 9704 开发套件深入探索
  • 人工智能与云计算双轮驱动:元宇宙如何重构全球产业生态
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 主页-评论用户名词云图实现
  • 亚纳米级检测!潜望式棱镜的“检测密码”,决定手机远景清晰度
  • 4G手机控车模块的核心功能与应用价值
  • 4种灵活的方法从POCO手机中删除联系人
  • 天邑TY1613_S905L3SB_安卓9-高安非-高安版-通刷-TTL线刷固件包
  • 考研初试专业分146!上岸新疆大学!信号与系统考研经验,通信考研小马哥。
  • 图机器学习(20)——欺诈交易检测算法
  • python基础:操作列表
  • 2024年ASOC SCI2区TOP,基于强化学习教与学优化算法RLPS-TLBO+风电场布局优化,深度解析+性能实测
  • 一种宏模板实现方法
  • (实用攻略)Linux操作系统(一)
  • 强制缓存与协商缓存
  • CentOS7 安装 rust 1.82.0
  • C语言转义字符‘\\‘‘ 解析与常见误区
  • 收银系统合作模式全解析:SaaS、私有化部署与开源版选型指南
  • Kylin V10 4070安装nvidia驱动+CUDA+docker安装
  • 循环队列的两种实现
  • Spring AI 系列之二十四 - ModerationModel
  • 每日算法刷题Day52:7.24:leetcode 栈5道题,用时1h35min
  • 前端性能新纪元:Rust + WebAssembly 如何在浏览器中实现10倍性能提升(以视频处理为例)
  • uniapp nvue开发App 横竖屏切换丢失上下文导致 setTimeout和clearTimeout报错
  • [网安工具] 自动化威胁检测工具 —— D 盾 · 使用手册
  • SAP-MM-采购订单批量创建 excel 版
  • 保留5位小数封装一个自定义指令
  • Linux 内核基础统简全解:Kbuild、内存分配和地址映射
  • 10分钟搭建脚手架:Spring Boot 3.2 + Vue3 前后端分离模板