Minio 属性类(放置在common model)
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author 梦欣
* @version 1.0
* @description MinIo属性配置类
* @date 2023/2/10 22:52
*/
@Data
@Component
@ConfigurationProperties(
prefix = "minio"
)
public class MinIoProperties {
@ApiModelProperty(value = "登陆用户名")
private String accessKey;
@ApiModelProperty(value = "登陆密码")
private String secretKey;
@ApiModelProperty(value = "服务端地址")
private String endpoint;
@ApiModelProperty(value = "桶对象")
Bucket bucket = new Bucket();
@Data
public class Bucket{
@ApiModelProperty(value = "存储文件的Bucket Names")
private String files;
@ApiModelProperty(value = "存储视频的Bucket Names")
private String video;
}
}
Nacos Minio属性配置
spring:
# 设置文件上传大小
servlet:
multipart:
enabled: true
max-file-size: 5TB
max-request-size: 5TB
# MinIo 属性配置
minio:
endpoint:
accessKey: mengxin
secretKey: mengxin520.*
bucket:
files: media-files # 存储文件的Bucket Names
video: media-video # 存储视频的Bucket Names
Minio Config配置类(放置在common model)
import com.meng.common.properties.MinioProperties;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* Minio 配置类
*/
@SpringBootConfiguration
@EnableConfigurationProperties(MinioProperties.class)
public class MinioConfig {
@Autowired
private MinioProperties minioProperties;
@Bean
public MinioClient createMinioClient() {
return MinioClient.builder()
.endpoint(minioProperties.getEndpoint())
.credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
.build();
}
}
Minio配置导入至微服务
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.ComponentScan;
/**
* 导入Minio 配置类
* @author 梦欣
*/
@SpringBootConfiguration
@ComponentScan(basePackages = {"com.meng.common.minio"})
public class ImportConfig {
}
Minio是通用的, 放置在common model中目的是为了方便, 不需要重复配置, 导入模块即可
Upload Controller(例)
/**
* 上传商品Log
*/
@PostMapping("/image")
public ResponseEntity<String> uploadImage(@RequestParam("file") MultipartFile file){
log.info("上传商品图片log: {}", file);
return ResponseEntity.ok(uploadService.uploadImage(file));
}
Upload Service(例)
import com.meng.common.exception.constant.ExceptionEnum;
import com.meng.common.exception.entity.MengException;
import com.meng.common.properties.MinioProperties;
import com.meng.upload.service.UploadService;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.StatObjectArgs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 文件上传
* @author 梦欣
*/
@Service
public class UploadServiceImpl implements UploadService {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioProperties minioProperties;
@Override
@ApiOperation(value = "文件上传, 通用接口")
public MediaFileUploadVo uploadCourseFile(Long companyId, UploadFileParamsDto uploadFileParamsDto, byte[] bytes, String folder, String objectName) {
// 创建文件Id, MD5值
String fileId = DigestUtils.md5Hex(bytes);
// 若文件夹名称为null, 则根据日期条件参数为文件夹名称
if (StringUtils.isBlank(folder)) {
folder = getFolderName(new Date(), true, true, true) + companyId.toString() + "/";
} else if (!folder.endsWith("/")) {
// 若指定的目录中的尾部没有 / , 则添加斜杠
folder = folder + "/";
}
// 若文件名称为null, 则设定 随机MD5值 + 用户上传的文件名称的扩展名 为默认值
if (StringUtils.isBlank(objectName)) {
objectName = fileId + uploadFileParamsDto.getFilename().substring(uploadFileParamsDto.getFilename().lastIndexOf("."));
}
// 获取文件字节输出流, 用于上传使用
InputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
// 构建对象写入请求响应对象 及 视图响应对象
ObjectWriteResponse objectWriteResponse;
MediaFileUploadVo mediaFileUploadVo;
try {
// 构建文件上传参数
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(minIoProperties.getBucket().getFiles())
.object(folder + objectName)
.stream(byteArrayInputStream, byteArrayInputStream.available(), -1)
.contentType(uploadFileParamsDto.getContentType())
.build();
// 上传文件
objectWriteResponse = minioClient.putObject(putObjectArgs);
log.info("文件上传信息: {}", objectWriteResponse.headers());
// 查询数据库中是否有当前上传文件信息
MediaFiles mediaFiles = getById(fileId);
// 若查询结果为null, 则是新上传文件, 若不为null, 则说明这个文件已经上传过, 是重复上传, 不需要修改文件信息
if (ObjectUtils.isEmpty(mediaFiles)) {
// 构建MediaFiles对象, 插入到Media表中, new一个新对象的原因是因为旧对象中的值已经为null, 而不是空值
mediaFiles = new MediaFiles();
BeanUtils.copyProperties(uploadFileParamsDto, mediaFiles);
mediaFiles.setId(fileId);
mediaFiles.setCompanyId(companyId);
mediaFiles.setCompanyName("梦欣");
mediaFiles.setFilePath(folder + objectName);
mediaFiles.setFileId(fileId);
mediaFiles.setUrl(minIoProperties.getBucket().getFiles() + "/" + folder + objectName);
mediaFiles.setBucket(objectWriteResponse.bucket());
mediaFiles.setStatus("1");
mediaFiles.setCreateDate(LocalDateTime.now());
mediaFiles.setChangeDate(LocalDateTime.now());
mediaFiles.setAuditStatus(DictionaryConstants.COURSE_AUDIT_STATUS_PASS_AUDIT);
mediaFiles.setAuditMind("审核通过");
// 插入到MediaFiles表
boolean saveResult = save(mediaFiles);
// 若插入失败, 则抛出异常
if (!saveResult) {
throw new XuechengEduException(ExceptionEnum.INSERT_OPERATION_FAIL);
}
}
// 构建视图对象
mediaFileUploadVo = BeanHelper.copyProperties(mediaFiles, MediaFileUploadVo.class);
// 若拷贝失败, 则抛出异常
if (ObjectUtils.isEmpty(mediaFileUploadVo)) {
throw new XuechengEduException(ExceptionEnum.OBJECT_COPY_ERROR);
}
} catch (Exception e) {
throw new XuechengEduException(ExceptionEnum.FILE_UPLOAD_ERROR);
}
return mediaFileUploadVo;
}
// 根据设定的日期条件拼接目录名
public String getFolderName(Date date, Boolean year, Boolean month, Boolean day) {
// 获取当前日期的字符串
String format = new SimpleDateFormat("yyyy-MM-dd").format(date);
// 取出年月日
String[] dateArray = format.split("-");
StringBuilder folderName = new StringBuilder();
// 只要年份
if (year) {
folderName.append(dateArray[0]).append("/");
}
// 只要月份
if (month) {
folderName.append(dateArray[1]).append("/");
}
// 只要日期
if (day) {
folderName.append(dateArray[2]).append("/");
}
return folderName.toString();
}
/**
* 判断文件是否存在
* @param bucketName 桶名称
* @param fileName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
* @return true存在, 反之
*/
public Boolean checkFilesExist(String bucketName, String fileName){
// 判断上传的文件是否已存在
try {
minioClient.statObject(
StatObjectArgs
.builder()
.bucket(bucketName)
.object(fileName)
.build()
);
} catch (Exception e) {
return false;
}
return true;
}
}