文章目录
- 搭建高性能分布式存储-minio
- Docker搭建minio(单机部署+纠删码模式)⭐
- 创建minio的bucket(桶)⭐
- SpringBoot+minio项目实战 ⭐
- 1:导入minio的maven依赖
- 2:编写MinioProperties.class
- 3:application.yml:
- 4:MinioConfig.class配置类
- 5:ResponseResult(统一响应结果)
- 6:ResponseType(响应类型枚举类)
- 7:UploadController.class(上传接口)
- 8:上传service接口
- 9:上传service接口实现类:
搭建高性能分布式存储-minio
Docker搭建minio(单机部署+纠删码模式)⭐
- 1:使用minio Docker镜像,在在单机中使用EC纠删码模式部署,8块盘中启动minio服务:
- 注意:MINIO_ROOT_PASSWORD初始化默认密码长度必须要达到8位。
docker run --name minio -d \
-p 9000:9000 \
-p 50000:50000 \
-v /mnt/minio/data1:/data1 \
-v /mnt/minio/data2:/data2 \
-v /mnt/minio/data3:/data3 \
-v /mnt/minio/data4:/data4 \
-v /mnt/minio/data5:/data5 \
-v /mnt/minio/data6:/data6 \
-v /mnt/minio/data7:/data7 \
-v /mnt/minio/data8:/data8\
-e MINIO_ROOT_USER="root" \
-e MINIO_ROOT_PASSWORD="12345678" \
minio/minio:latest server /data{1...8} --console-address ":50000"
- 2:访问minio控制台:(http://服务器ip:50000/)
创建minio的bucket(桶)⭐
SpringBoot+minio项目实战 ⭐
1:导入minio的maven依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
2:编写MinioProperties.class
package com.boot.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 高性能分布式存储-minio属性类
*
* @author youzhengjie
* @date 2022/10/28 00:22:48
*/
@Component
@Data
@ConfigurationProperties(prefix = "minio")
@EnableConfigurationProperties({
MinioProperties.class
})
public class MinioProperties {
/**
* minio的通信端点(url),和控制台端点不一样
*/
private String endpoint;
/**
* 说白了就是minio的帐号
*/
private String accessKey;
/**
* 说白了就是minio的密码
*/
private String secretKey;
/**
* 指定操作哪一个桶
*/
private String bucketName;
}
3:application.yml:
- 注意:记得把下面的配置属性修改成你自己的!
minio:
endpoint: http://192.168.184.123:9000
access-key: root
secret-key: 12345678
bucket-name: security-jwt2-bucket
4:MinioConfig.class配置类
- 作用是将MinioClient交给Spring管理,方便后续直接@AutoWired注入即可使用)
package com.boot.config;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* minio配置类
*
* @author youzhengjie
* @date 2022/10/28 00:30:31
*/
@Configuration
public class MinioConfig {
@Autowired
private MinioProperties minioProperties;
/**
* minio-client bean
*
* @return {@link MinioClient}
*/
@Bean
public MinioClient minioClient(){
return MinioClient.builder()
//配置minio的通信端点(url),和控制台端点不一样
.endpoint(minioProperties.getEndpoint())
//配置minio的帐号密码
.credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
.build();
}
}
5:ResponseResult(统一响应结果)
package com.boot.data;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 统一响应结果
*/
@JsonInclude(JsonInclude.Include.NON_NULL) //为null的字段不进行序列化
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseResult<T> {
/**
* 响应状态码
*/
private Integer code;
/**
* 响应状态码对应的信息提示
*/
private String msg;
/**
* 返回给前端的数据
*/
private T data;
public ResponseResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}
}
6:ResponseType(响应类型枚举类)
package com.boot.enums;
/**
* 响应类型枚举类
* @author youzhengjie
* @date 2022-09-22 22:47:21
*/
public enum ResponseType {
/**
* 文件操作状态
*/
IMAGE_UPLOAD_SUCCESS(901,"图片上传成功"),
IMAGE_UPLOAD_ERROR(902,"图片上传失败"),
FILE_FORMAT_UNSUPPORT(903,"不支持该文件格式,上传失败"),
FILE_DELETE_SUCCESS(904,"文件删除成功"),
FILE_DELETE_ERROR(905,"文件删除失败"),
;
private int code;
private String message;
ResponseType(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
7:UploadController.class(上传接口)
@RestController
@Api("上传接口")
@RequestMapping(path = "/upload")
@Slf4j
public class UploadController {
@Autowired
@Qualifier("minioUploadServiceImpl") //指定spring注入的实现类
private OssUploadService ossUploadService;
/**
* 上传头像
*
* @param avatarFile 头像文件(名字一定要和el-upload的:name属性一致)
* @return {@link ResponseResult}
*/
@OperationLog("上传头像")
@PostMapping(path = "/avatar")
@ApiOperation("上传头像")
public ResponseResult uploadAvatar(MultipartFile avatarFile){
ResponseResult result = ossUploadService.imageUpload(avatarFile);
return result;
}
/**
* 文件删除
*
* @param fileFullName 文件名
* @return {@link ResponseResult}
*/
@OperationLog("文件删除")
@DeleteMapping(path = "/fileDelete")
@ApiOperation("文件删除")
public ResponseResult fileDelete(@RequestParam("fileFullName") String fileFullName){
return ossUploadService.fileDelete(fileFullName);
}
/**
* 文件下载
*
* @param fileName 文件名称
* @param response 响应
*/
@GetMapping(path = "/fileDownload")
@ApiOperation("文件下载")
public void fileDownload(@RequestParam("fileName") String fileName, HttpServletResponse response){
ossUploadService.fileDownload(fileName,response);
}
}
8:上传service接口
package com.boot.service;
import com.boot.data.ResponseResult;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
/**
* oss上传service接口
* @author youzhengjie
* @date 2022-10-06 23:13:28
*/
public interface OssUploadService {
/**
* oss图片上传
* @param imageFile
* @return 上传结果
*/
ResponseResult imageUpload(MultipartFile imageFile);
/**
* oss文件删除
* @param fileFullName 文件全名,
*
* @return 删除结果
*/
ResponseResult fileDelete(String fileFullName);
/**
* 文件下载
*
* @param fileName 文件名称
* @param response 响应
*/
default void fileDownload(String fileName, HttpServletResponse response){
throw new UnsupportedOperationException("该实现类暂不支持文件下载操作,请切换到其他实现类!");
}
}
9:上传service接口实现类:
package com.boot.service.impl;
import com.boot.config.MinioProperties;
import com.boot.data.ResponseResult;
import com.boot.enums.ResponseType;
import com.boot.service.OssUploadService;
import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
/**
* minio上传服务service impl
*
* @author youzhengjie
* @date 2022/10/28 13:59:35
*/
@Service("minioUploadServiceImpl")
@Slf4j
public class MinioUploadServiceImpl implements OssUploadService {
@Autowired
private MinioProperties minioProperties;
/**
* 注入MinioClient,用于操作minio
*/
@Autowired
private MinioClient minioClient;
/**
* 检查文件是否是图片类型
* @param originalFilename
* @return true代表是图片,false则不是图片
*/
private boolean isImage(String originalFilename){
//将文件名全部变小写
String lowerOriginalFilename = originalFilename.toLowerCase();
return lowerOriginalFilename.endsWith(".jpg") ||
lowerOriginalFilename.endsWith(".png") ||
lowerOriginalFilename.endsWith(".jpeg");
}
/**
* minio图片上传
*
* @param imageFile 图像文件
* @return {@link ResponseResult}
*/
@Override
public ResponseResult imageUpload(MultipartFile imageFile) {
//封装响应结果
ResponseResult<Object> result = new ResponseResult<>();
try {
//获取上传前的文件原名
String oldFileName = imageFile.getOriginalFilename();
//如果不是图片则直接返回
if(!isImage(oldFileName)){
result.setCode(ResponseType.FILE_FORMAT_UNSUPPORT.getCode());
result.setMsg(ResponseType.FILE_FORMAT_UNSUPPORT.getMessage());
return result;
}
//以日期作为目录,每一天的图片都会放到不同的目录下,方便管理
String fileDir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd/"));
//UUID文件名
String uuidFileName = UUID.randomUUID().toString().replaceAll("-", "");
//获取文件后缀名 .jpg
String fileSuffix= oldFileName.substring(oldFileName.lastIndexOf("."));
//上传到minio中的新的图片文件名
String newFileName = new StringBuilder()
.append(fileDir)
.append(uuidFileName)
.append(fileSuffix).toString();
//获取文件流
InputStream inputStream = imageFile.getInputStream();
//获取文件大小
long size = imageFile.getSize();
//获取内容类型
String contentType = imageFile.getContentType();
//构建文件上传所需要的东西(PutObjectArgs)
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(minioProperties.getBucketName())
.object(newFileName)
.stream(inputStream, size, -1)
.contentType(contentType)
.build();
//开始进行minio文件上传
minioClient.putObject(putObjectArgs);
//获取该上传到minio的文件的url(使外网可以访问)
String fileUrl=minioProperties.getEndpoint()+"/"+minioProperties.getBucketName()+"/"+newFileName;
result.setCode(ResponseType.IMAGE_UPLOAD_SUCCESS.getCode());
result.setMsg(ResponseType.IMAGE_UPLOAD_SUCCESS.getMessage());
result.setData(fileUrl);
return result;
}catch (Exception e){
e.printStackTrace();
result.setCode(ResponseType.IMAGE_UPLOAD_ERROR.getCode());
result.setMsg(ResponseType.IMAGE_UPLOAD_ERROR.getMessage());
return result;
}
}
/**
* minio文件删除
*
* @param fileFullName 文件全名 。格式例如:2022/10/28/4f74aa358a4548d4860c110ebec3831f.jpg
* @return {@link ResponseResult}
*/
@Override
public ResponseResult fileDelete(String fileFullName) {
//封装响应结果
ResponseResult<Object> result = new ResponseResult<>();
try {
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
.bucket(minioProperties.getBucketName())
.object(fileFullName)
.build();
minioClient.removeObject(removeObjectArgs);
result.setCode(ResponseType.FILE_DELETE_SUCCESS.getCode());
result.setMsg(ResponseType.FILE_DELETE_SUCCESS.getMessage());
return result;
}catch (Exception e){
e.printStackTrace();
result.setCode(ResponseType.FILE_DELETE_ERROR.getCode());
result.setMsg(ResponseType.FILE_DELETE_ERROR.getMessage());
return result;
}
}
/**
* minio文件下载。
*
* @param fileName 文件名称 。格式例如:2022/10/28/4f74aa358a4548d4860c110ebec3831f.jpg
* @param response 响应
*/
@Override
public void fileDownload(String fileName, HttpServletResponse response) {
try {
// 获取对象信息
StatObjectArgs statObjectArgs = StatObjectArgs.builder()
.bucket(minioProperties.getBucketName())
.object(fileName)
.build();
StatObjectResponse statObject = minioClient.statObject(statObjectArgs);
/**
* 描述: content-type 指示响应内容的格式
* content-disposition 指示如何处理响应内容。
* 一般有两种方式:
* inline:直接在页面显示-预览
* attachment:以附件形式下载-下载
*/
response.setContentType(statObject.contentType());
// response.setHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.setHeader("Content-Disposition", "attachment; filename=" +
URLEncoder.encode(fileName, "UTF-8"));
InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
.bucket(minioProperties.getBucketName())
.object(fileName)
.build());
IOUtils.copy(inputStream,response.getOutputStream());
}catch (Exception e){
throw new RuntimeException("文件下载失败");
}
}
}