javajavaSpringBoot整合Minio文件存储服务
kikockMinIO简介
MinIO 是一款基于 Go 语言发开的高性能、分布式的对象存储系统,客户端支持 Java,Net,Python,Javacript,Golang语言。
MinIO 的主要目标是作为私有云对象存储的标准方案,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据、容器和虚拟机镜像等,而一个对象文件可以是任意大小,从几 kb 到最大 5T。
它具有分布式,高可用性和水平扩展的特点,它非常适合用于大规模数据存储和分析。其优点包括低延迟、高吞吐量、易于部署和管理。
截止目前,MinIO 在 Github 上有 43.7k Star。
国内阿里巴巴、腾讯、百度、华为、中国移动、中国联通等企业都有在使用 MinIO,甚至不少商业公司二次开发 MinIO 来提供商业化的云存储产品。
使用 Docker 部署 MinIO 服务
安装Docker&Compose(略)
使用docker-compose安装并运行MinIO容器
docker-compose.yaml 文件如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| networks: minio-network: external: true services: minio: container_name: minio-data environment: MINIO_ROOT_PASSWORD: minio_admin MINIO_ROOT_USER: minio_kikock image: minio/minio:latest networks: - minio-network ports: - 9001:9001 - 9000:9000 restart: always volumes: - ./data:/data - ./certs:/root/.minio/certs
|
命令行解释:
networks网络
services服务
- container_name: 设置容器名称为 minio-data。
- environment: 指定了 Minio 根用户账号 (minio_kikock) 及密码 (minio_admin)。
- image: 使用官方的 Minio 镜像最新版本 (minio/minio:latest)。
- networks: 将服务加入到 minio-network 网络。
- ports: 映射主机端口 9001 到容器端口 9001 (用于 Minio 浏览器) 和 9000 到 9000 (用于 Minio 服务 API)。
- restart: 设置为 always 以确保容器异常退出后自动重启。
- volumes: 挂载主机目录 ./data 和 ./certs 到容器相应目录,保持数据和证书的持久化。
将yaml文件上传到服务器执行docker-compose -up -d 创建并运行容器
ps: 也可以使用1Panel服务器管理控制面板安装更简单
最后在浏览器中访问http://服务器IP:9001,即可访问到MinIO的控制台。**
可以输入账号 admin,密码 password 进行登录,进入首页。

创建一个 Bucket 存储桶,用于稍后文件的上传操作。


创建并保存好生成Access Key 和 Secret Key**.


至此,我们已经基本完成了MinIO的简单配置,当然如果想要更安全,同样是可以像使用第三方对象存储服务一样创建用户组,分配权限等诸如此类的操作。
测试一下
上传一张图片并访问来测试一下是否搭建成功。


可以看到成功上传了图片点击这里可以预览

接下来我们在浏览器访问 http://服务器IP:9000/{bucket存储桶名字}/{name图片后缀名} 来进行访问。
sdn.png,所以最终的访问路径是 http://服务器IP:9000/guanzhi/csdn.png。
Spring boot集成Minio并应用
一、依赖的引入
1.1、maven项目
1 2 3 4 5
| <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.5.11</version> </dependency>
|
二、minio配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Data @Configuration public class MinioConfig{ @Value("${file.minio.endpoint}") private String endpoint; @Value("${file.minio.accessKey}") private String accessKey; @Value("${file.minio.secretKey}") private String secretKey; @Value("${file.minio.bucket}") private String bucketName;
@Bean public MinioClient minioClient(){ MinioClient minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build(); return minioClient; }
}
|
其中四个变量为application.yml中的配置,MinioConfig.java配置文件创建了minio分布式存储系统的操作客户端minioClient并交给springboot进行管理,且创建了名为java-bucket的桶。
1 2 3 4 5 6 7 8 9 10
| file: minio: endpoint: http://127.0.0.1:9001 accessKey: minioadmin secretKey: minioadmin bucket: java-bucket
|
三、通过minioClient操作文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| @Service @Slf4j public class MinioUtils{ @Autowired MinioConfig minioConfig; @Autowired MinioClient minioClient;
public List<String> listObjects(){ List<String> list = new ArrayList<>(); try {
ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder() .bucket(minioConfig.getBucketName()) .build();
Iterable<Result<Item>> results = minioClient.listObjects(listObjectsArgs); for (Result<Item> result : results) { Item item = result.get(); log.info(item.lastModified() + ", " + item.size() + ", " + item.objectName()); list.add(item.objectName()); } } catch (Exception e) { log.error("错误:" + e.getMessage()); } return list; }
public void deleteObject(String objectName){ try { RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder() .bucket(minioConfig.getBucketName()) .object(objectName) .build(); minioClient.removeObject(removeObjectArgs); } catch (Exception e) { log.error("错误:" + e.getMessage()); } }
public void uploadObject(InputStream is, String fileName, String contentType){ try { PutObjectArgs putObjectArgs = PutObjectArgs.builder() .bucket(minioConfig.getBucketName()) .object(fileName) .contentType(contentType) .stream(is, is.available(), -1) .build(); minioClient.putObject(putObjectArgs); is.close(); } catch (Exception e) { log.error("错误:" + e.getMessage()); } }
public String getObjectUrl(String objectName){ try { GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder() .method(Method.GET) .bucket(minioConfig.getBucketName()) .object(objectName) .expiry(7, TimeUnit.DAYS) .build(); return minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs); } catch (Exception e) { e.printStackTrace(); log.error("错误:" + e.getMessage()); } return ""; }
public InputStream getObject(String objectName){ try { GetObjectArgs getObjectArgs = GetObjectArgs.builder() .bucket(minioConfig.getBucketName()) .object(objectName) .build(); return minioClient.getObject(getObjectArgs); } catch (Exception e) { log.error("错误:" + e.getMessage()); } return null; }
}
|
四、通过MinioFileController进行调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| @RestController @RequestMapping("/minio") @Tag(name = "MinIO储存库接口") public class MinioFileController{
@Autowired MinioUtils minioService;
@Autowired MinioConfig minioConfig; @GetMapping("/list") @Operation(summary = "获取MinIO储存库附件列表") public AjaxResult list(){ List<String> strings = minioService.listObjects(); return AjaxResult.success(strings); }
@PutMapping("/delete") @Operation(summary = "删除MinIO储存库附件") @Parameters({ @Parameter(name = "filename", description = "文件名称", required = true, in = ParameterIn.QUERY) }) public AjaxResult delete(@RequestParam String filename){ minioService.deleteObject(filename); return AjaxResult.success(true); }
@PostMapping("/upload") @Operation(summary = "上传附件到MinIO储存库") @Parameters({ @Parameter(name = "file", description = "文件", required = true, in = ParameterIn.QUERY) }) public AjaxResult upload(@RequestParam("file") MultipartFile file){ try { StringBuilder sb =new StringBuilder(); sb.append(minioConfig.getEndpoint()).append("/").append(minioConfig.getBucketName()).append("/"); InputStream is = file.getInputStream(); String originalFilename = file.getOriginalFilename(); String newFileName = DateUtils.dateTime()+System.currentTimeMillis() + "." + StringUtils.substringAfterLast(originalFilename, "."); String contentType = file.getContentType(); sb.append(newFileName); minioService.uploadObject(is, newFileName, contentType); AjaxResult ajax = AjaxResult.success(); ajax.put("url", sb.toString()); ajax.put("fileName", sb.toString()); ajax.put("newFileName", newFileName); ajax.put("originalFilename", originalFilename); return ajax; } catch (Exception e) { throw new ServiceException("上传失败",HttpStatus.ERROR); } }
@GetMapping("/download/{filename}") @Operation(summary = "下载MinIO储存库附件") @Parameters({ @Parameter(name = "filename", description = "文件名称", required = true, in = ParameterIn.QUERY) }) public void download(@PathVariable("filename") String filename, HttpServletResponse response){ try { InputStream fileInputStream = minioService.getObject(filename); String newFileName = System.currentTimeMillis() + "." + StringUtils.substringAfterLast(filename, "."); response.setHeader("Content-Disposition", "attachment;filename=" + newFileName); response.setContentType("application/force-download"); response.setCharacterEncoding("UTF-8"); IOUtils.copy(fileInputStream, response.getOutputStream()); } catch (Exception e) { throw new ServiceException("下载失败",HttpStatus.ERROR); } }
@GetMapping("/getHttpUrl") @Operation(summary = "获取MinIO储存库附件地址") public AjaxResult getHttpUrl(@RequestParam String filename){ try { String url = minioService.getObjectUrl(filename); return AjaxResult.success(url); } catch (Exception e) { throw new ServiceException( e.getMessage(),HttpStatus.ERROR); } }
}
|
五、最后调用接口测试一下
