# MinIODemo **Repository Path**: suyi-oh/min-iodemo ## Basic Information - **Project Name**: MinIODemo - **Description**: SpringBoot整合MinIO. - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-06-24 - **Last Updated**: 2024-06-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: 后端框架 ## README # MinIO ## 什么是MinIO?他有什么用途? ​ MinIO是一个开源的对象存储系统,专为云原生和容器化环境而设计。它使用分布式架构来存储和管理大规模的非结构化数据,例如图片、视频、日志文件等。MinIO基于[Amazon S3协议](Amazon Simple Storage Service (Amazon S3) 是一种面向 Internet 的存储服务。您可以通过 Amazon S3 随时在 Web 上的任何位置存储和检索任意大小的数据。),并提供了与S3兼容的API,这使得它能够与现有的S3生态系统无缝集成。 ## MinIO的特点 #### 1.高性能: ​ 作为高性能对象存储,在标准硬件条件下它能达到55GB/s的读、35GB/s的写速率; #### 2.可扩容: ​ 不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并跨越多个数据中心; #### 3.SDK支持: 基于Minio轻量的特点,它得到类似Java、Python或Go等语言的sdk支持; #### 4.支持纠删码: ​ MinIO使用[纠删码](编码容错技术)、[Checksum](是一种用于数据校验的算法)来防止硬件错误和静默数据污染。在最高冗余度配置下,即使丢失1/2的磁盘也能恢复数据; ## MinIO基本概念 - bucket(桶) :相当于文件夹 - Object : 相当于文件 - Keys :相当于文件名 - MINIO_ACCESS_KEY:访问key,类似账号; - MINIO_SECRET_KEY:秘钥,类似密码。 | 存储方式 | 优点 | 缺点 | | :---------------------------: | :------------------------: | :------: | | 服务器磁盘 | 开发便捷,成本低 | 扩展困难 | | 分布式文件系统 | 容易实现扩展 | 复杂度高 | | 第三方存储(阿里云OSS、七牛云) | 开发简单,功能强大,免维护 | 收费 | ## MinIO环境搭建 ##### 1.拉取镜像 ~~~dockerfile docker pull minio/minio ~~~ ​ 成功后显示: ![1718798286536](C:\Users\20641\AppData\Roaming\Typora\typora-user-images\1718798286536.png) ##### 1.2配置docker镜像 1. 查看本地是否存在`/etc/docker/daemon.json`这个配置文件 ###### 存在 复制下面的镜像地址到配置文件中 ~~~shell "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://docker.mirrors.ustc.edu.cn", "https://docker.nju.edu.cn" ] ~~~ ###### 不存在 ~~~shell #1. 进入目录 -- 如果没有创建 mkdir -p /etc/docker #2. 写入镜像配置 -- 直接复制粘贴 tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://docker.mirrors.ustc.edu.cn", "https://docker.nju.edu.cn" ] } EOF #3. 重启服务 systemctl daemon-reload systemctl restart docker ~~~ ###### 完成 ~~~shell #1. 查看是否配置成功 docker info ~~~ ​ 成功后在文件最下方会有: ![1718798163693](C:\Users\20641\AppData\Roaming\Typora\typora-user-images\1718798163693.png) ##### 2. 启动及配置MinIO ~~~shell #先创建minio 文件存放的位置 mkdir -p /opt/docker/minio/data #启动并指定端口 docker run \ -p 5000:9000 \ -p 5001:5001 \ --name minio \ -v /opt/docker/minio/data:/data \ -e "MINIO_ROOT_USER=root" \ -e "MINIO_ROOT_PASSWORD=12345678" \ -d minio/minio server /data --console-address ":5001" ~~~ - -p 5000:9000 ,端口映射 - -e,环境变量 - -d,后台运行 - –name,给容器起名字 - –restart=always,开机自启 - -e “MINIO_ACCESS_KEY=root”,设置账号 - -e “MINIO_SECRET_KEY=root”,设置密码 - -v 挂载(绑定)数据卷 ##### 3. 测试访问 ​ 测试访问MinIO后台系统,直接浏览器访问`http://ip:5001`即可。 ##### 4. 指令操作 ~~~shell #以交互模式执行容器 docker exec -it minio /bin/bash #列出存储桶 mc ls #创建存储桶 mc mb / #上传文件 mc cp / #下载文件 mc cp // #复制对象 mc cp #移动对象 mc mv #删除对象 mc rm // #删除存储桶 mc rb / ~~~ ## SpringBoot整合MinIO #### 依赖项 ~~~java org.springframework.boot spring-boot-starter-web org.projectlombok lombok io.minio minio 8.2.2 org.apache.commons commons-lang3 3.11 ~~~ #### application.yml配置项 ~~~java minio: endpoint: http://192.168.136.128:5000 //地址端口 accessKey: root //账号 secretKey: 12345678 //密码 bucketName: suyi //桶名称 ~~~ #### MinIOConfig配置类 ~~~java @Data @Configuration public class MinioConfig { @Value("${minio.endpoint}") private String endpoint; @Value("${minio.accessKey}") private String accessKey; @Value("${minio.secretKey}") private String secretKey; @Value("${minio.bucketName}") private String bucketName; @Bean public MinioClient minioClient() { MinioClient minioClient = MinioClient.builder() .endpoint(endpoint) .credentials(accessKey, secretKey) .build(); return minioClient; } ~~~ #### MinIOUtil工具类 ~~~java @Component @Slf4j public class MinioUtil { @Autowired private MinioConfig prop; @Resource private MinioClient minioClient; /** * 查看存储bucket是否存在 * * @return boolean */ public Boolean bucketExists(String bucketName) { Boolean found; try { found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); } catch (Exception e) { e.printStackTrace(); return false; } return found; } /** * 创建存储bucket * * @return Boolean */ public Boolean makeBucket(String bucketName) { try { minioClient.makeBucket(MakeBucketArgs.builder() .bucket(bucketName) .build()); } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 删除存储bucket * * @return Boolean */ public Boolean removeBucket(String bucketName) { try { minioClient.removeBucket(RemoveBucketArgs.builder() .bucket(bucketName) .build()); } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 获取全部bucket */ public List getAllBuckets() { try { List buckets = minioClient.listBuckets(); List list = new ArrayList<>(); buckets.forEach(bucket -> { list.add(bucket.name()); }); return list; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 文件上传 * * @param file 文件 * @return Boolean */ public String upload(MultipartFile file) { String originalFilename = file.getOriginalFilename(); if (StringUtils.isBlank(originalFilename)) { throw new RuntimeException(); } String fileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf(".")); String objectName = LocalDate.now() + "/" + fileName; try { PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(prop.getBucketName()).object(objectName) .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build(); //文件名称相同会覆盖 minioClient.putObject(objectArgs); } catch (Exception e) { e.printStackTrace(); return null; } return objectName; } /** * 预览图片 * * @param fileName * @return */ public String preview(String fileName) { // 查看文件地址 GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(prop.getBucketName()).object(fileName).method(Method.GET).build(); try { String url = minioClient.getPresignedObjectUrl(build); return url; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 文件下载 * * @param fileName 文件名称 * @param res response * @return Boolean */ public void download(String fileName, HttpServletResponse res) { GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(prop.getBucketName()) .object(fileName).build(); try (GetObjectResponse response = minioClient.getObject(objectArgs)) { byte[] buf = new byte[1024]; int len; try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) { while ((len = response.read(buf)) != -1) { os.write(buf, 0, len); } os.flush(); byte[] bytes = os.toByteArray(); res.setCharacterEncoding("utf-8"); // 设置强制下载不打开 // res.setContentType("application/force-download"); res.addHeader("Content-Disposition", "attachment;fileName=" + fileName); try (ServletOutputStream stream = res.getOutputStream()) { stream.write(bytes); stream.flush(); } } } catch (Exception e) { e.printStackTrace(); } } /** * 查看文件对象 * * @return 存储bucket内文件对象信息 */ public List listObjects() { Iterable> results = minioClient.listObjects( ListObjectsArgs.builder().bucket(prop.getBucketName()).build()); List items = new ArrayList<>(); try { for (Result result : results) { items.add(result.get()); } } catch (Exception e) { e.printStackTrace(); return null; } return items; } /** * 删除 * * @param fileName * @return * @throws Exception */ public boolean remove(String fileName) { try { minioClient.removeObject(RemoveObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build()); } catch (Exception e) { return false; } return true; } ~~~ #### FileController控制层 ~~~java @Slf4j @RestController @RequestMapping("api/") public class FileController { @Autowired private MinioUtil minioUtil; @Autowired private MinioConfig prop; @Autowired HttpServletResponse res; //列出所有桶名称 @GetMapping("/getAllBuckets") public List getAllBuckets() { return minioUtil.getAllBuckets(); } //查看存储bucket是否存在 @GetMapping("/bucketExists") public boolean bucketExists(@RequestParam("bucketName") String bucketName) { return minioUtil.bucketExists(bucketName); } //创建存储bucket @GetMapping("/makeBucket") public boolean makeBucket(@RequestParam("bucketName") String bucketName) { return minioUtil.makeBucket(bucketName); } //删除存储bucket @GetMapping("/removeBucket") public boolean removeBucket(@RequestParam("bucketName") String bucketName) { return minioUtil.removeBucket(bucketName); } //文件上传返回url @PostMapping("/upload") public String upload(@RequestParam("file") MultipartFile file) { String objectName = minioUtil.upload(file); if (null != objectName) { return prop.getEndpoint() + "/" + prop.getBucketName() + "/" + objectName; } return "上传失败"; } //图片/视频预览 @GetMapping("/preview") public String preview(@RequestParam("fileName") String fileName) { return minioUtil.preview(fileName); } //文件下载") @GetMapping("/download") public String download(@RequestParam("fileName") String fileName) { minioUtil.download(fileName,res); return "下载成功"; } //删除文件 根据url地址删除文件 @PostMapping("/delete") public boolean remove(@RequestParam("url") String url) { String objName = url.substring(url.lastIndexOf(prop.getBucketName()+"/") + prop.getBucketName().length()+1); return minioUtil.remove(objName); } ~~~ ## 参考手册 #### [MinIO文档](https://www.bookstack.cn/read/MinioCookbookZH/22.md#makeBucket)