一、Linux 安装Minio
安装
在/root/xxkfz/soft目录下面创建文件minio文件夹,进入minio文件夹,并创建data目录;
[root@xxkfz soft]# mkdir minio
[root@xxkfz soft]# cd minio
[root@xxkfz minio]# mkdir data
执行如下命令进行下载
[root@xxkfz minio]# wget https://dl.min.io/server/minio/release/linux-amd64/minio
[root@xxkfz minio]# chmod +x minio # 赋权
下载完成后如下所示:
设置账号密码
minio 默认账号密码为 minioadmin/minioadmin
[root@xxkfz minio]# export MINIO_ACCESS_KEY=admin # 设置控制台账号(最少3位)
[root@xxkfz minio]# export MINIO_SECRET_KEY=12345678 # 设置密码(最少8位)
直接设置管理员账号密码 编辑 /etc/profile 文件即可
[root@xxkfz minio]# vim /etc/profile
编辑/etc/profile文件,追加如下内容:
# set minio environment
export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=admin123
启动
进入执行文件目录/root/xxkfz/soft/minio,自定义端口启动(默认端口:9000)
[root@xxkfz minio]# nohup /root/xxkfz/soft/minio/minio server --address :9001--console-address :9002/root/xxkfz/soft/minio/data>/root/xxkfz/soft/minio/minio.log 2>&1 &
说明:
- nohup 为后台启动
- ./minio server 启动命令
- –address :9001 指定API端口
- –console-address :9002 指定控制台端口
- /usr/local/minio/data 指定存储目录
- /usr/local/minio/minio.log 2>&1 控制台日志重定向
- 到/usr/local/minio/minio.log文件中
- & 后台运行
启动成功:
注意:浏览器访问需要开启防火墙端口!
阿里云配置开放9001、9002端口
测试访问:http://IP地址:9002
输入账号密码: admin/12345678 登录成功!
设置开机自启动
设置Minio服务器宕机后自动重启
进入init.d目录
[root@xxkfz minio]# cd /etc/rc.d/init.d
新建minio.sh shell脚本文件
[root@xxkfz init.d]# vim minio.sh
shell脚本内容
#!/bin/bash#chkconfig: 2345 10 90#description: ping10nohup /root/xxkfz/soft/minio/minio server --address :9001 --console-address :9002 /root/xxkfz/soft/minio/data >/root/xxkfz/soft/minio/minio.log 2>&1&
给shell脚本赋权
chmod +x minio.sh
添加到开机自启动服务中
chkconfig--add minio.sh
设置开机自启动
chkconfig minio.sh on
查看是否添加成功
chkconfig--list
二、Spring Boot整合Minio
项目搭建
项目基本结构
引入依赖
pom.xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.11</version></dependency>
配置MinIo
application.yml
minio:endpoint: http://IP地址:9001accessKey: admin
secretKey:12345678bucketName: xk-admin
配置端口号
server:port:8099
编写配置类
MinioConfig.java
/**
* @program: xxkfz-minio
* @ClassName MinioConfig.java
* @author: 公众号:小小开发者
* @create: 2024-03-13 10:53
* @description: Minio 配置类
**/@Data@ConfigurationpublicclassMinioConfig{/**
* 访问地址
*/@Value("${minio.endpoint}")privateString endpoint;/**
* accessKey类似于用户ID,用于唯一标识你的账户
*/@Value("${minio.accessKey}")privateString accessKey;/**
* secretKey是你账户的密码
*/@Value("${minio.secretKey}")privateString secretKey;/**
* 默认存储桶
*/@Value("${minio.bucketName}")privateString bucketName;@BeanpublicMinioClientminioClient(){MinioClient minioClient =MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();return minioClient;}}
编写Minio操作工具类
MinioUtils.java
/**
* @program: xxkfz-minio
* @ClassName MinioUtils.java
* @author: 公众号:小小开发者
* @create: 2024-03-13 10:55
* @description: MinIO操作工具类
**/@Slf4j@ComponentpublicclassMinioUtils{@AutowiredprivateMinioClient minioClient;/**
* 启动SpringBoot容器的时候初始化Bucket
* 如果没有Bucket则创建
*
* @param bucketName
*/publicvoidcreateBucket(String bucketName){try{if(!bucketExists(bucketName)){
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
log.info("创建bucketName = {}完成!", bucketName);return;}
log.info("bucketName = {}已存在!策略为:{}", bucketName,getBucketPolicy(bucketName));}catch(Exception e){
log.error("创建bucketName = {}异常!e = {}", bucketName, e);}}/**
* 判断Bucket是否存在,true:存在,false:不存在
*
* @param bucketName
* @return
*/@SneakyThrowspublicbooleanbucketExists(String bucketName){return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());}/**
* 获得Bucket的策略
*
* @param bucketName
* @return
*/@SneakyThrowspublicStringgetBucketPolicy(String bucketName){return minioClient.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build());}/**
* 获得所有Bucket列表
*
* @return
*/@SneakyThrowspublicList<Bucket>getAllBuckets(){return minioClient.listBuckets();}/**
* 根据bucketName获取其相关信息
*
* @param bucketName
* @return
*/@SneakyThrows(Exception.class)publicOptional<Bucket>getBucket(String bucketName){returngetAllBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();}/**
* 根据bucketName删除Bucket,true:删除成功; false:删除失败,文件或已不存在
*
* @param bucketName
* @throws Exception
*/@SneakyThrows(Exception.class)publicvoidremoveBucket(String bucketName){
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());}/**
* 判断文件是否存在
*
* @param bucketName
* @param objectName
* @return
*/publicbooleanisObjectExist(String bucketName,String objectName){boolean exist =true;try{
minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());}catch(Exception e){
log.error("[Minio工具类]>>>> 判断文件是否存在, 异常:", e);
exist =false;}return exist;}/**
* 判断文件夹是否存在
*
* @param bucketName
* @param objectName
* @return
*/publicbooleanisFolderExist(String bucketName,String objectName){boolean exist =false;try{Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());for(Result<Item> result : results){Item item = result.get();if(item.isDir()&& objectName.equals(item.objectName())){
exist =true;}}}catch(Exception e){
log.error("[Minio工具类]>>>> 判断文件夹是否存在,异常:", e);
exist =false;}return exist;}/**
* 根据文件前置查询文件
*
* @param bucketName 存储桶
* @param prefix 前缀
* @param recursive 是否使用递归查询
* @return MinioItem 列表
*/@SneakyThrows(Exception.class)publicList<Item>getAllObjectsByPrefix(String bucketName,String prefix,boolean recursive){List<Item> list =newArrayList<>();Iterable<Result<Item>> objectsIterator = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());if(objectsIterator !=null){for(Result<Item> o : objectsIterator){Item item = o.get();
list.add(item);}}return list;}/**
* 获取文件流
*
* @param bucketName 存储桶
* @param objectName 文件名
* @return 二进制流
*/@SneakyThrows(Exception.class)publicInputStreamgetObject(String bucketName,String objectName){return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());}/**
* 断点下载
*
* @param bucketName 存储桶
* @param objectName 文件名称
* @param offset 起始字节的位置
* @param length 要读取的长度
* @return 二进制流
*/@SneakyThrows(Exception.class)publicInputStreamgetObject(String bucketName,String objectName,long offset,long length){return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(offset).length(length).build());}/**
* 获取路径下文件列表
*
* @param bucketName 存储桶
* @param prefix 文件名称
* @param recursive 是否递归查找,false:模拟文件夹结构查找
* @return 二进制流
*/publicIterable<Result<Item>>listObjects(String bucketName,String prefix,boolean recursive){return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());}/**
* 使用MultipartFile进行文件上传
*
* @param bucketName 存储桶
* @param file 文件名
* @param objectName 对象名
* @param contentType 类型
* @return
*/@SneakyThrows(Exception.class)publicObjectWriteResponseuploadFile(String bucketName,MultipartFile file,String objectName,String contentType){InputStream inputStream = file.getInputStream();return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).contentType(contentType).stream(inputStream, inputStream.available(),-1).build());}/**
* 图片上传
*
* @param bucketName
* @param imageBase64
* @param imageName
* @return
*/publicObjectWriteResponseuploadImage(String bucketName,String imageBase64,String imageName){if(!StringUtils.isEmpty(imageBase64)){InputStream in =base64ToInputStream(imageBase64);String newName =System.currentTimeMillis()+"_"+ imageName +".jpg";String year =String.valueOf(newDate().getYear());String month =String.valueOf(newDate().getMonth());returnuploadFile(bucketName, year +"/"+ month +"/"+ newName, in);}returnnull;}publicstaticInputStreambase64ToInputStream(String base64){ByteArrayInputStream stream =null;try{byte[] bytes =Base64.getEncoder().encode(base64.trim().getBytes());
stream =newByteArrayInputStream(bytes);}catch(Exception e){
e.printStackTrace();}return stream;}/**
* 上传本地文件
*
* @param bucketName 存储桶
* @param objectName 对象名称
* @param fileName 本地文件路径
* @return
*/@SneakyThrows(Exception.class)publicObjectWriteResponseuploadFile(String bucketName,String objectName,String fileName){return minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());}/**
* 通过流上传文件
*
* @param bucketName 存储桶
* @param objectName 文件对象
* @param inputStream 文件流
* @return
*/@SneakyThrows(Exception.class)publicObjectWriteResponseuploadFile(String bucketName,String objectName,InputStream inputStream){return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(inputStream, inputStream.available(),-1).build());}/**
* 创建文件夹或目录
*
* @param bucketName 存储桶
* @param objectName 目录路径
* @return
*/@SneakyThrows(Exception.class)publicObjectWriteResponsecreateDir(String bucketName,String objectName){return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(newByteArrayInputStream(newbyte[]{}),0,-1).build());}/**
* 获取文件信息, 如果抛出异常则说明文件不存在
*
* @param bucketName 存储桶
* @param objectName 文件名称
* @return
*/@SneakyThrows(Exception.class)publicStringgetFileStatusInfo(String bucketName,String objectName){return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()).toString();}/**
* 拷贝文件
*
* @param bucketName 存储桶
* @param objectName 文件名
* @param srcBucketName 目标存储桶
* @param srcObjectName 目标文件名
*/@SneakyThrows(Exception.class)publicObjectWriteResponsecopyFile(String bucketName,String objectName,String srcBucketName,String srcObjectName){return minioClient.copyObject(CopyObjectArgs.builder().source(CopySource.builder().bucket(bucketName).object(objectName).build()).bucket(srcBucketName).object(srcObjectName).build());}/**
* 删除文件
*
* @param bucketName 存储桶
* @param objectName 文件名称
*/@SneakyThrows(Exception.class)publicvoidremoveFile(String bucketName,String objectName){
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());}/**
* 批量删除文件
*
* @param bucketName 存储桶
* @param keys 需要删除的文件列表
* @return
*/publicvoidremoveFiles(String bucketName,List<String> keys){List<DeleteObject> objects =newLinkedList<>();
keys.forEach(s ->{
objects.add(newDeleteObject(s));try{removeFile(bucketName, s);}catch(Exception e){
log.error("[Minio工具类]>>>> 批量删除文件,异常:", e);}});}/**
* 获取文件外链
*
* @param bucketName 存储桶
* @param objectName 文件名
* @param expires 过期时间 <=7 秒 (外链有效时间(单位:秒))
* @return url
*/@SneakyThrows(Exception.class)publicStringgetPresignedObjectUrl(String bucketName,String objectName,Integer expires){GetPresignedObjectUrlArgs args =GetPresignedObjectUrlArgs.builder().expiry(expires).bucket(bucketName).object(objectName).build();return minioClient.getPresignedObjectUrl(args);}/**
* 获得文件外链
*
* @param bucketName
* @param objectName
* @return url
*/@SneakyThrows(Exception.class)publicStringgetPresignedObjectUrl(String bucketName,String objectName){GetPresignedObjectUrlArgs args =GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).method(Method.GET).build();return minioClient.getPresignedObjectUrl(args);}/**
* 将URLDecoder编码转成UTF8
*
* @param str
* @return
* @throws UnsupportedEncodingException
*/publicStringgetUtf8ByURLDecoder(String str)throwsUnsupportedEncodingException{String url = str.replaceAll("%(?![0-9a-fA-F]{2})","%25");returnURLDecoder.decode(url,"UTF-8");}}
项目启动初始化配置
创建配置类InitConfig.java,并实现了InitializingBean接口,重写afterPropertiesSet方法。
该方法主要实现逻辑:在项目启动的时候初始化Bucket,如果没有则进行创建!
InitConfig.java
/**
* @program: xxkfz-minio
* @ClassName Init.java
* @author: wust
* @create: 2024-03-16 10:34
* @description: 项目启动初始化配置
**/@Component@Slf4jpublicclassInitConfigimplementsInitializingBean{@AutowiredprivateMinioUtils minioUtils;@AutowiredprivateMinioConfig minioConfig;@OverridepublicvoidafterPropertiesSet()throwsException{// 项目启动创建Bucket,不存在则进行创建
minioUtils.createBucket(minioConfig.getBucketName());}}
编写测试接口
MinioController.java
/**
* @program: xxkfz-minio
* @ClassName OSSController.java
* @author: wust
* @create: 2024-03-13 11:01
* @description:
**/@Slf4j@RestController@RequestMapping("/oss")publicclassMinioController{@AutowiredprivateMinioUtils minioUtils;@AutowiredprivateMinioConfig minioConfig;/**
* 文件上传
*
* @param file
*/@PostMapping("/upload")publicStringupload(@RequestParam("file")MultipartFile file){try{//文件名String fileName = file.getOriginalFilename();String newFileName =System.currentTimeMillis()+"."+StringUtils.substringAfterLast(fileName,".");//类型String contentType = file.getContentType();
minioUtils.uploadFile(minioConfig.getBucketName(), file, newFileName, contentType);return"上传成功,文件名:"+ newFileName;}catch(Exception e){
e.printStackTrace();return"上传失败";}}/**
* 删除
*
* @param fileName
*/@DeleteMapping("/")publicvoiddelete(@RequestParam("fileName")String fileName){
minioUtils.removeFile(minioConfig.getBucketName(), fileName);}/**
* 获取文件信息
*
* @param fileName
* @return
*/@GetMapping("/info")publicStringgetFileStatusInfo(@RequestParam("fileName")String fileName){return minioUtils.getFileStatusInfo(minioConfig.getBucketName(), fileName);}/**
* 获取文件外链
*
* @param fileName
* @return
*/@GetMapping("/url")publicStringgetPresignedObjectUrl(@RequestParam("fileName")String fileName){return minioUtils.getPresignedObjectUrl(minioConfig.getBucketName(), fileName);}/**
* 文件下载
*
* @param fileName
* @param response
*/@GetMapping("/download")publicvoiddownload(@RequestParam("fileName")String fileName,HttpServletResponse response){try{InputStream fileInputStream = minioUtils.getObject(minioConfig.getBucketName(), fileName);
response.setHeader("Content-Disposition","attachment;filename="+ fileName);
response.setContentType("application/force-download");
response.setCharacterEncoding("UTF-8");IOUtils.copy(fileInputStream, response.getOutputStream());}catch(Exception e){
log.error("下载失败");}}}
测试验证
启动项目:
上传图片
测试接口:http://localhost:8099/oss/upload
进入服务器查看文件上传情况。
进入目录:/root/xxkfz/soft/minio/data/xk-admin
当然,也可以直接访问minio的地址:http://IP地址:9001/xk-admin/1710558001536.jpg。验证文件是否上传成功。
获取文件信息
测试接口:http://localhost:8099/oss/info
获取文件外链
测试接口:http://localhost:8099/oss/url
下载文件
测试接口:http://localhost:8099/oss/download
版权归原作者 断春风 所有, 如有侵权,请联系我们删除。