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

山东大学软件学院项目实训-创新实训-计科智伴(二)——个人博客(MinIO配置)

项目背景


在我们的项目开发过程中,需要一个可靠的文件存储解决方案来管理用户上传的图片和其他文件。传统的本地文件存储方式在扩展性、可靠性和安全性方面存在诸多限制,因此我们决定引入对象存储服务。经过调研,我们选择了MinIO作为我们的对象存储解决方案。

接入过程


1. 环境准备


首先,我们需要在本地或服务器上安装并启动MinIO服务。MinIO提供了多种安装方式,我们选择了Docker方式进行部署:

docker run -p 9000:9000 -p 9090:9090 --name minio \ -v /mnt/data:/data \ -e "MINIO_ROOT_USER=minioadmin" \ -e "MINIO_ROOT_PASSWORD=minioadmin" \ minio/minio server /data --console-address ":9090"

启动后,可以通过 http://localhost:9090 访问MinIO控制台进行管理。

2. 项目集成

2.1 添加依赖
在项目的 pom.xml 文件中添加MinIO客户端依赖:

<dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.5.2</version> </dependency>

2.2 配置文件
创建 minio.properties 配置文件,存放MinIO服务的连接信息:

minio.url=http://127.0.0.1:9000 minio.username=minioadmin minio.password=minioadmin minio.bucketName=img

2.3 配置类实现
创建 MinioConfig 类,用于读取配置并初始化MinIO客户端:

package com.example.Config; import io.minio.MinioClient; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class MinioConfig { private String url; private String username; private String password; private String bucketName; { final Properties properties = new Properties(); final InputStream is = MinioConfig.class.getResourceAsStream("/minio.properties"); try { properties.load(is); } catch (IOException e) { e.printStackTrace(); } url = properties.getProperty("minio.url"); username = properties.getProperty("minio.username"); password = properties.getProperty("minio.password"); bucketName = properties.getProperty("minio.bucketName"); } public String getBucketName() { return bucketName; } public MinioClient getMinioClient() { MinioClient minioClient = MinioClient.builder().endpoint(url) .credentials(username, password).build(); return minioClient; } }

2.4 工具类实现
创建 MinioUtil 类,封装MinIO的各种操作方法:

package com.example.utils; import com.example.Config.MinioConfig; import com.google.common.io.ByteStreams; import io.minio.*; import io.minio.http.Method; import io.minio.messages.Bucket; import io.minio.messages.DeleteError; import io.minio.messages.DeleteObject; import io.minio.messages.Item; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.*; @Slf4j @Component public class MinioUtil { private MinioClient minioClient; private String bucketName; { final MinioConfig minioConfig = new MinioConfig(); minioClient = minioConfig.getMinioClient(); bucketName = minioConfig.getBucketName(); } // 桶操作 public boolean bucketExists() { try { return minioClient.bucketExists(BucketExistsArgs.builder() .bucket(bucketName) .build()); } catch (Exception e) { log.error("查看bucket是否存在", e); return false; } } public boolean createBucket() { if (bucketExists()) { return true; } try { minioClient.makeBucket(MakeBucketArgs.builder() .bucket(bucketName) .build()); return true; } catch (Exception e) { log.error("创建桶失败", e); return false; } } // 文件操作 public String putObject(MultipartFile multipartFile, String folderName, String aimFileName) { if (!bucketExists()) { createBucket(); } if (!StringUtils.hasText(aimFileName)) { aimFileName = UUID.randomUUID().toString(); } // 获取文件后缀 String originalFilename = multipartFile.getOriginalFilename(); String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); aimFileName += suffix; // 带路径的文件名 String lastFileName = ""; if (StringUtils.hasText(folderName)) { lastFileName = "/" + folderName + "/" + aimFileName; } else { lastFileName = aimFileName; } try (InputStream inputStream = multipartFile.getInputStream();) { // 上传文件到指定目录,文件名称相同会覆盖 minioClient.putObject(PutObjectArgs.builder() .bucket(bucketName) .object(lastFileName) .stream(inputStream, multipartFile.getSize(), -1) .contentType(multipartFile.getContentType()) .build()); return getObjectUrl(lastFileName); } catch (Exception e) { log.error("文件上传失败", e); return null; } } public void getObject(String fileName, HttpServletResponse response) { if (!bucketExists()) { return; } GetObjectArgs getObjectArgs = GetObjectArgs.builder() .bucket(bucketName) .object(fileName) .build(); try (ServletOutputStream outputStream = response.getOutputStream(); GetObjectResponse objectResponse = minioClient.getObject(getObjectArgs)) { response.setCharacterEncoding("utf-8"); response.addHeader("Content-Disposition", "attachment;filename=" + fileName); ByteStreams.copy(objectResponse, outputStream); outputStream.flush(); } catch (Exception e) { log.error("文件下载失败", e); } } public String getObjectUrl(String fileName) { try { if (fileName.startsWith("/")) { fileName = fileName.substring(1); } GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder() .bucket(bucketName) .object(fileName) .method(Method.GET) // 过期时间(分钟数) .expiry(60 * 60) .build(); return minioClient.getPresignedObjectUrl(build); } catch (Exception e) { log.error("获取文件路径失败", e); } return null; } // 其他操作方法... }

2.5 控制器集成
在 DispatcherController 中添加文件上传和下载接口:

package com.example.Controller; import com.example.utils.MinioUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; @Controller public class DispatcherController { @Autowired private MinioUtil minioUtil; @PostMapping("/fileUpload") public void fileUpload(MultipartFile imgFile) { final String s = minioUtil.putObject(imgFile); System.out.println(s); } @ResponseBody @GetMapping("/fileDownload/{filename}") public void download(@PathVariable("filename") String filename, HttpServletResponse response) { minioUtil.getObject(filename, response); } }

功能特性


我们实现的MinIO工具类支持以下功能:

1. 桶操作 :创建桶、检查桶是否存在、删除桶、获取桶信息等
2. 文件操作 :上传文件、下载文件、获取文件信息、删除文件等
3. 目录操作 :创建目录、检查目录是否存在等
4. URL生成 :生成带签名的临时访问URL


遇到的问题与解决方案


在接入过程中,我们遇到了几个问题:

1. 依赖冲突 :MinIO客户端依赖与项目中其他依赖存在冲突,通过调整依赖版本解决。
2. 权限配置 :默认情况下,MinIO桶的访问权限可能不够,需要在控制台中设置合适的权限。
3. 文件路径处理 :在处理文件路径时,需要注意斜杠的处理,确保路径格式正确。

总结


通过本次实践,我们成功地将MinIO对象存储服务集成到了项目中,为我们的应用提供了可靠的文件存储解决方案。MinIO的优势在于:

1. 高性能 :MinIO采用Go语言编写,性能优异,适合处理大量文件存储。
2. 易于部署 :可以通过Docker快速部署,也支持各种云平台。
3. API友好 :提供了丰富的SDK,方便与各种语言集成。
4. 兼容S3 :完全兼容Amazon S3 API,方便未来可能的迁移。
5. 开源免费 :作为开源项目,可以免费使用。
通过封装的工具类,我们的应用可以方便地进行文件的上传、下载、管理等操作,为用户提供更好的体验。

http://www.cnnetsun.cn/news/2000818.html

相关文章:

  • ST7735S Datasheet里的‘黑话’:SPI时序、0x36寄存器与Gamma曲线,一次搞懂驱动屏的那些关键配置
  • Win11Debloat终极指南:3步轻松清理Windows系统,提升50%性能
  • 终极指南:3分钟快速检测微信单向好友,轻松清理“僵尸“好友
  • K210人脸识别项目实战:如何用SD卡实现人脸特征值的断电保存与加载?
  • C# 14 AOT编译Dify客户端:为什么你的AOT二进制仍加载慢?6个被忽略的IL trimming陷阱曝光
  • 重装系统后,Android Studio老项目报CMake版本错误?三步搞定NDK路径配置(附local.properties详解)
  • 5个步骤快速掌握KMS_VL_ALL_AIO:Windows与Office批量激活终极指南
  • 【硬件知识】光模块学习
  • LeetCode 1855.下标对中的最大距离:双指针
  • Python的__getattr__在属性访问链中的递归调用问题
  • 【2026年最新600套毕设项目分享】微信小程序的电影院订票选座系统(30118)
  • 从流体力学到临床:一文搞懂FFR(血流储备分数)的计算原理与核心价值
  • 避坑指南:在Cesium中构建清晰三维管道网络,如何解决实体重叠与视觉混乱问题?
  • 汽修厂最怕你发现的秘密武器!只输个车型,汽车毛病怎么修全都有
  • MCU逐波保护( CBC)技术
  • 3分钟快速安装TrollStore:TrollInstallerX终极指南
  • 保姆级教程:在Ubuntu 20.04上搞定GAMIT/GLOBK 10.7安装(含gfortran版本避坑指南)
  • 开源移动端性能测试测试工具推荐-perfara
  • “方向盘没松开就答错”?Dify注意力掩码机制深度解析:如何用3行配置实现驾驶专注度感知式应答降频(实测降低误唤醒率76%)
  • 抖音去水印批量下载终极指南:如何3分钟搞定无水印视频批量下载
  • VideoSrt:如何为视频创作者解决字幕制作难题的智能语音识别解决方案
  • HiveWE:重新定义魔兽争霸III地图制作的终极智能编辑器
  • EM菌在水产养殖中的作用与优质产品推荐
  • 从‘学生选课’到‘商品订单’:手把手带你用MySQL实战理解关系代数(选择、投影、连接)
  • 告别实体卡!Android 系统级 SIM 卡模拟:CarrierTestOverride 机制深度解读与自定义配置
  • 用MATLAB搞定无人机/机器人轨迹规划:手把手实现Minimum Snap(附完整代码)
  • 从“七桥问题”到算法面试:离散数学中的图论到底怎么用?
  • 别再被CPK和PPK搞晕了!用Minitab做过程能力分析,这3个坑新手必踩(附真实数据案例)
  • 告别手动配置!在Ubuntu 22.04上一条命令搞定PyQt5和Qt Designer开发环境
  • 医学图像分割神器ITK-SNAP:为什么8000+研究论文都选择这款开源工具?