SpringBoot整合Minio实现文件上传、下载:
1,介绍高性能分布式存储文件服务Minio:Minio是基于Go语言编写的对象存储服务,适合于存储大容量非结构化的数据,例如图片、音频、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。区别于分布式存储系统,minio的特色在于简单、轻量级,对开发者友好!!!,认为存储应该是一个开发问题而不是一个运维问题。

Minio的高可用设计特性:

  • 分布式设计

1加粗样式、Minio将多个硬盘(可以分布在不同机器)组成一个分布式的对象存储系统
2、去中心化、分布式

  • 数据保护

1、Minio采用纠删码(erasuer code)来保证集群多点或者单点故障和位衰减(bit rot)
2、最少需要4个节点来使Minio自动引入纠删码

  • 高可用

分布式Minio存储系统允许集群N/2节点宕机,但是恢复数据则需要通过N/2+1节点来进行恢复。
例如:8个节点,宕机4个,则需要5个节点进行数据恢复

  • 限制

分布式Minio单个租户最小需要4个节点(硬盘),最大支持16个节点(硬盘)受限于纠删码(erasuer
code),我们可以使用k8s来进行minio租户的管理。

  • 一致性

Minio不论是分布式或者是单机,均遵从read-after-write来保证数据的一致性

  • 安全

支持TLS证书

  • Minio纠删码

Minio使用纠删码(erasuer code) 进行校验来保护数据,
避免发生硬件故障或者某种原因导致数据丢失,即使丢失N/2 的硬盘都可以将数据恢复回来。
http://www.360doc.com/content/21/1004/21/25921839_998266807.shtml 通过这个链接了解纠删码

Minio如何使用纠删码及优点

纠删码是一种数据丢失和损坏数据的一种算法,Minio使用Reed-Solomon
code拆分对象为N/2数据和N/2奇偶校验块。
例如:我有12块硬盘,存放了一个文件,Minio会给我分成6份数据和6个奇偶校验块,不管我是丢失了哪个硬盘,数据均可以得到恢复。
.
Minio可以针对某个对象进行单独恢复,而raid则需要针对卷来进行恢复,从而减少了恢复的时间周期。
Minio对每个对象进行编码,存储服务一经部署,存储硬盘一般不需要进行更换或者修复,Minio的纠删码设计目标是为了性能和硬件加速。

接下来单机版Docker部署Minio服务:

通过配置docker-compose.yml文件管理容器,通过docker-compose up -d 启动容器

docker-compose.yml文件
这里的9100是配置访问minio自带的web管理后台,9090是配置的服务器

version: "3.1"
services:minio:image: minio/minio:latestcontainer_name: minioports:- "9100:9000"- "9090:9090"volumes:- "./data:/data"environment:MINIO_ACCESS_KEY: "root"MINIO_SECRET_KEY: "12345678"command: server /data --console-address=":9000" --address=":9090"logging:driver: "json-file"options:max-size: "1m"

启动容器,访问web界面,如图所示


.
.

minio服务搭建完毕之后,接下来就是用Springboot整合Minio实现文件的上传、下载
项目练习源码gitee地址:
https://gitee.com/xzq25_com/minio-test.git

项目目录结构:

拉取以上项目源码,简单总结一下需要干的事儿:

1,yml文件配置
2,minio配置类并注入容器进行管理
3,封装minio操作服务器的API的工具类,项目中封装了单文件上传、下载、删除、查看功能,桶的创建等
公司具体的存储业务,在次基础上根据公司业务来自行扩展
4,编写controller层,用postman进行简单的测试(测试类型:文本、文档、表格、音频、视频、图片)

yml配置

spring:# 配置文件上传大小限制servlet:multipart:max-file-size: 200MBmax-request-size: 200MB
minio:server: http://120.76.159.196port: 9090accessKey: rootsecretKey: 12345678bucket: testurlprefix: /minio/

配置类截图如下:

/*** @Author xiaozq* @Date 2022/11/17 9:54* <p>@Description: 注意:这里不能用@Data,需手动写gitter,setter* reason:与@ConfigurationProperties一起用的时候,配置文件的值不能赋值给对应类属性,属性值均为null</p>*/
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {private String server;private int port;private String accessKey;private String secretKey;// 桶名private String bucket;// 统一前缀private String urlPrefix;/*** 创建minio连接对象* @return*/@Beanpublic MinioClient minioClient(){return  MinioClient.builder().endpoint(server,port,false).credentials(accessKey,secretKey).build();}public void setServer(String server) {this.server = server;}public void setPort(int port) {this.port = port;}public void setAccessKey(String accessKey) {this.accessKey = accessKey;}public void setSecretKey(String secretKey) {this.secretKey = secretKey;}public void setBucket(String bucket) {this.bucket = bucket;}public void setUrlPrefix(String urlPrefix) { this.urlPrefix = urlPrefix; }public String getUrlPrefix() {return urlPrefix;}public String getServer() {return server;}public int getPort() {return port;}public String getAccessKey() {return accessKey;}public String getSecretKey() {return secretKey;}public String getBucket() {return bucket;}
}

自定义封装操作minio的api工具类

*** @Author xiaozq* @Date 2022/8/5 10:42* <p>@Description:</p>*/
@Component
public class MinioTemplate {@Autowiredprivate MinioClient minioClient;@Value("${minio.bucket}")public String bucketName;@Value("${minio.urlprefix}")public String urlprefix;/*** 判断bucket是否存在,不存在则创建* @param name*/public void existBucket(String name){try {boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());if(!exist){minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());}}catch (Exception e){e.printStackTrace();}}/*** 创建存储bucket* @param bucketName 存储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* @param bucketName 存储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;}/*** 文件上传* @param file* @return*/public Map<String, String> upload(MultipartFile file) {String filename = FileUtils.extractUploadFilename(file);try {InputStream inputStream = file.getInputStream();// 上传到minio服务器minioClient.putObject(PutObjectArgs.builder().bucket(this.bucketName).object(filename).stream(inputStream, -1L, 10485760L).build());} catch (Exception e) {e.printStackTrace();}// 返回地址Map<String,String > resultMap = new HashMap<>();resultMap.put("url",filename);return  resultMap;}/*** 文件下载* @param fileName 文件名* @param delete   是否删除* @throws IOException*/public void fileDownload(@RequestParam(name = "fileName") String fileName,@RequestParam(defaultValue = "false") Boolean delete,HttpServletResponse response) {InputStream inputStream   = null;OutputStream outputStream = null;try {if (StringUtils.isBlank(fileName)) {response.setHeader("Content-type", "text/html;charset=UTF-8");String data = "文件下载失败";OutputStream ps = response.getOutputStream();ps.write(data.getBytes("UTF-8"));return;}outputStream = response.getOutputStream();// 获取文件对象inputStream =minioClient.getObject(GetObjectArgs.builder().bucket(this.bucketName).object(fileName).build());byte buf[] = new byte[1024];int length = 0;response.reset();response.setHeader("Content-Disposition", "attachment;filename=" +URLEncoder.encode(fileName.substring(fileName.lastIndexOf("/") + 1), "UTF-8"));response.setContentType("application/octet-stream");response.setCharacterEncoding("UTF-8");// 输出文件while ((length = inputStream.read(buf)) > 0) {outputStream.write(buf, 0, length);}inputStream.close();// 判断:下载后是否同时删除minio上的存储文件if (BooleanUtils.isTrue(delete)) {minioClient.removeObject(RemoveObjectArgs.builder().bucket(this.bucketName).object(fileName).build());}} catch (Throwable ex) {response.setHeader("Content-type", "text/html;charset=UTF-8");String data = "文件下载失败";try {OutputStream ps = response.getOutputStream();ps.write(data.getBytes("UTF-8"));}catch (IOException e){e.printStackTrace();}} finally {try {outputStream.close();if (inputStream != null) {inputStream.close();}}catch (IOException e){e.printStackTrace();}}}/*** 查看文件对象* @param bucketName 存储bucket名称* @return 存储bucket内文件对象信息*/public List<ObjectItem> listObjects(String bucketName) {Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());List<ObjectItem> objectItems = new ArrayList<>();try {for (Result<Item> result : results) {Item item = result.get();ObjectItem objectItem = new ObjectItem();objectItem.setObjectName(item.objectName());objectItem.setSize(item.size());objectItems.add(objectItem);}} catch (Exception e) {e.printStackTrace();return null;}return objectItems;}/*** 批量删除文件对象* @param bucketName 存储bucket名称* @param objects 对象名称集合*/public Map<String, String> removeObjects(String bucketName, List<String> objects) {Map<String,String > resultMap = new HashMap<>();List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());try {minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());resultMap.put("mes","删除成功");}catch (Exception e){e.printStackTrace();resultMap.put("mes","网络异常,删除失败");}return resultMap;}}

上传文件名替换工具类:

public class FileUtils {/*** 编码文件名* 日期路径 + UUID* 示例:fileName=2022/11/18/统计报表1668758006562.txt*/public static final String extractUploadFilename(MultipartFile file){String fileName = file.getOriginalFilename();// 注意,这里需要加上 \\ 将 特殊字符 . 转意 \\. ,否则异常String[] fileArray = fileName.split("\\.");fileName = datePath() + "/" + fileArray[0]+System.currentTimeMillis()+"."+fileArray[1];return fileName;}/*** 日期路径 即年/月/日 如2018/08/08*/public static final String datePath() {Date now = new Date();return DateFormatUtils.format(now, "yyyy/MM/dd");}
}

controller层测试

@RestController
public class MinioController {@Autowiredprivate MinioTemplate minioTemplate;@Value("${minio.server}")private String server;@Value("${minio.port}")private Integer port;@Value("${minio.bucket}")private String bucket;/*** 单文件上传* @param file 文件* @return*/@PostMapping("/uploadone")public Object uploadOne(MultipartFile file) {return minioTemplate.upload(file);}/***  单文件下载* @param fileName 文件* @param delete 下载完后是否删除, 请谨慎传参*/@GetMapping("/download")public void download(@RequestParam(value = "fileName") String fileName,@RequestParam(defaultValue = "false")Boolean delete, HttpServletResponse response) {minioTemplate.fileDownload(fileName,delete,response);}/*** 查看存储的文件列表* @param bucket 桶* @return*/@GetMapping("/list")public  Object fileList(@RequestParam(value = "bucket") String bucket) {return minioTemplate.listObjects(bucket);}/*** 删除文件* @param bucket 桶* @param list 文件名列表* @return*/@DeleteMapping ("/remove")public  Object fileremove(@RequestParam String bucket,@RequestParam List<String> list) {return minioTemplate.removeObjects(bucket,list);}}

.

接下来就是用postman测试,非常便利:测试存储文档、表格、日志、视频、音频、图片等上传、下载

.

好啦,Minio的入门学习就到此结束啦,希望能给你带来帮助…

SpringBoot整合Minio实现文件上传、下载相关推荐

  1. java文件流 m.jb51.net_FasfDFS整合Java实现文件上传下载功能实例详解

    今天使用Java代码实现文件的上传和下载.对此作者提供了Java API支持,下载fastdfs-client-java将源码添加到项目中.或者在Maven项目pom.xml文件中添加依赖 org.c ...

  2. 【Java编程系列】Minio实现文件上传下载

    热门系列: [Java编程系列]Amazon S3实现文件上传下载 目录 热门系列: 1.前言 2.Minio实战代码 2.1 Minio环境部署 2.2 Minio的Sdk对接实现 2.2.1 Mi ...

  3. minio实现文件上传下载和删除功能

    前言 之前用到文件上传功能,在这里做个学习记录.使用minio实现,后面会记录使用fastdfs和阿里云的oss实现文件上传以及他们的比较(oss根据流量收费).minio的中文文档:https:// ...

  4. minio操作,文件上传下载

    minio操作工具类(其中,ip,端口,账号,密码在配置文件中配置) @Api(value = "minio操作") @Component public class UploadF ...

  5. springboot 整合 oss进行文件上传

    提示:没开通的先开通 操作OSS云服务 1.进入管理控制台 2.创建bucket 3.设置内容 4.上传文件 5.扫描上传文件 6.查看文件详情 7.点击头像,选择AccessKey管理 8.继续使用 ...

  6. centos nfs java_CentOS下安装配置NFS并通过Java进行文件上传下载

    1:安装NFS (1)安装 yum install nfs-utils rpcbind (2)启动rpcbind服务 systemctl restart rpcbind.service 查看服务状态 ...

  7. SpringMVC整合fastdfs-client-java实现web文件上传下载

    为什么80%的码农都做不了架构师?>>>    版权声明:本文为博主原创文章,转载请标明出处(http://blog.csdn.net/wlwlwlwl015)Thanks. 目录( ...

  8. java文件客户端下载_使用Java写一个minio的客户端上传下载文件

    标签:color   ati   tty   java   system   wired   format   media   param 前言: 确保已经安装了minio的服务端 代码: pom.x ...

  9. springboot文件上传下载实战 ——文件上传、下载、在线打开、删除

    springboot文件上传下载实战 文件上传 文件上传核心 UserFileController 文件上传测试 文件下载与在线打开 文件下载.在线打开核心 UserFileController 文件 ...

  10. springboot文件上传下载实战 —— 登录功能、展示所有文件

    springboot文件上传下载实战 创建项目 pom.xml 数据库建表与环境准备 建表SQL 配置文件 application.properties 整体架构 前端页面 登录页面 login.ht ...

最新文章

  1. WPF 快速制作可拖拽的对象和窗体
  2. JavaScript使用localStorage缓存Js和css文件
  3. bzoj 2251: [2010Beijing Wc]外星联络
  4. postman cookie设置_是时候抛弃Postman了,因为REST Client更香
  5. 信创产业发展应不忘初心牢记使命
  6. 并查集的补集 (关押罪犯)
  7. Android——处理设备旋转Activity销毁时临时数据丢失的问题
  8. 用python重温统计学基础:描述性统计分析
  9. linux mysql配置_Linux下MySQL安装配置 MySQL配置参数详解
  10. C4D快速入门教程——倒角
  11. 基于matlab的电力系统潮流计算,基于matlab电力系统潮流计算
  12. 实习商汤,校招华为,我的深度学习之路
  13. edge设置IE兼容模式
  14. Polkadot的PLO第一阶段: Equilibrium在DOT上筹集了850万美元
  15. EasyUi入门教程01
  16. vue图片懒加载插件vue-lazyload监听加载失败事件的解决方案
  17. 计算机亮度快捷键,电脑亮度怎么调(一个快捷键就可以设置亮度了)
  18. layui设置请求加载旋转等待
  19. CVPR2022-HairCLIP:基于文本和参考图像的头发编辑方法论文理解
  20. mysql workbench crows foot_一步一步设计你的数据库(三)

热门文章

  1. 中国农业大学计算机考研参考书目,中国农业大学(专业学位)计算机技术考研参考书目...
  2. java 及时释放内存_Java里可以自动释放的不只是内存,只要是“资源”,都可以自动释放!轻松加愉快!...
  3. python 每天定时运行程序(傻瓜式倒计时)
  4. 百度冰桶算法说明,如何避免冰桶算法呢?
  5. 后端学习路线的书籍经验总结
  6. parallelStream数据丢失问题
  7. sikuli实现百度云批量离线下载
  8. Taylor Swift -- Crazier 附英文和中文歌词
  9. php入侵检测,PHP入侵检测系统—PHPIDS
  10. kali 暴力破解 WiFi破解+fluxion安装使用(保姆级超详细)