minio开发参考
2023年4月18日大约 5 分钟
准备
- pom.xml
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.3.4</version>
</dependency>
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>- application.yml
# minio 参数配置
minio:
endpoint: http://localhost:20000
accessKey: abc
secretKey: 123456
# 默认存储桶
bucketName: test
# 浏览器访问文件url前缀
urlPrefix: ${minio.endpoint}/test/
# 压缩时,源文件的临时存放路径
temporaryFilePath: /data/minio/temporaryFile/- 配置类 MinIoConfig
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinIoConfig {
// minIo Server地址
private String endpoint;
// minIo 用户名
private String accessKey;
// minIo 密码
private String secretKey;
private String bucketName;
private String urlPrefix;
private String temporaryFilePath;
}minio 客户端
@Configuration
public class MinIoClientConfig {
@Resource
MinIoConfig config;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(config.getEndpoint())
.credentials(config.getAccessKey(), config.getSecretKey())
.build();
}
}minio 工具类
压缩、上传、指定存放位置
@Slf4j
@Component
public class MinioUtil {
@Resource
MinIoConfig minIoConfig;
private MinioUtil() {
}
/**
* 创建存储桶Bucket
*/
public void createBucket(MinioClient minioClient, String bucketName) {
try {
// 构建bucket权限为只读
StringBuilder builder = new StringBuilder();
builder.append("{\n");
builder.append(" \"Statement\": [\n");
builder.append(" {\n");
builder.append(" \"Action\": [\n");
builder.append(" \"s3:GetObject\"\n");
builder.append(" \"s3:GetBucketLocation\",\n");
builder.append(" \"s3:ListBucket\"\n");
builder.append(" ],\n");
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": \"*\",\n");
builder.append(" \"Resource\": \"arn:aws:s3:::").append(bucketName).append("\"\n");
builder.append(" },\n");
builder.append(" {\n");
builder.append(" \"Action\": \"s3:GetObject\",\n");
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": \"*\",\n");
builder.append(" \"Resource\": \"arn:aws:s3:::").append(bucketName).append("/*\"\n");
builder.append(" }\n");
builder.append(" ],\n");
builder.append(" \"Version\": \"2021-1-17\"\n");
builder.append("}\n");
// 创建存储桶
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName).build());
// 将存储桶策略配置设置为存储桶,并配置权限
minioClient.setBucketPolicy(
SetBucketPolicyArgs.builder().config(builder.toString()).bucket(bucketName).build());
//删除桶的加密配置
minioClient.deleteBucketEncryption(DeleteBucketEncryptionArgs.builder().bucket(bucketName).build());
//删除桶的标签。
minioClient.deleteBucketLifecycle(
DeleteBucketLifecycleArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
log.error("+++++++++++++++创建存储桶出现异常!+++++++++++++++");
log.error(e.getMessage());
}
}
/**
* 上传文件
*
* @param file 文件
* @param prefix 存放文件夹,如 “product”
*/
public JSONObject uploadFile(MultipartFile file, MinioClient minioClient, MinIoConfig config, String prefix) {
JSONObject object = new JSONObject();
try {
int width = 0;
int height = 0;
File newFile;
// 整理文件命名规则
String filename = file.getOriginalFilename();
if (filename.length() < 16) {
filename = IdUtil.objectId() + filename;
}
if (filename.length() > 32) {
filename = new StringBuilder().append(filename, filename.length() - 32, filename.length()).toString();
}
String objectName = new StringBuilder().append(StringUtils.isBlank(prefix) ? "" : prefix).append("/")
.append(DateUtil.format(new Date(), "yyyy-MM")).append("/")
.append(DateUtil.format(new Date(), "yyyy-MM-dd")).append("/").append(filename).toString();
// 压缩大于2m的图片
BufferedImage bufferedImage = ImageIO.read(file.getInputStream()); // 判断是否是图片
if ((1024 * 1024 * 2) < file.getSize() && bufferedImage != null) {
// 在项目根目录下的 upload 目录中生成临时文件
// newFile = new File("src/main/resources/upload/" + UUID.fastUUID() + filename);
newFile = new File(minIoConfig.getTemporaryFilePath() + UUID.fastUUID() + filename);
// 按比例【scale】缩小一半,画质【outputQuality】压缩一半 【越大越清晰,数值0.1~1】
Thumbnails.of(file.getInputStream()).scale(1f).outputQuality(1f).toFile(newFile);
// 将压缩后的文件转为 MultipartFile,重新赋值给file
file = new MockMultipartFile(file.getName(), filename, file.getContentType(), new FileInputStream(newFile));
// 压缩后的图片
BufferedImage newImg = ImageIO.read(file.getInputStream());
width = newImg.getWidth(); // 通过图片流获取图片宽度
height = newImg.getHeight(); // 通过图片流获取图片高度
log.info("图片已压缩!");
// 删除本地临时文件
newFile.delete();
}
// 上传到服务端
minioClient.putObject(PutObjectArgs.builder()
.object(objectName)
.contentType(file.getContentType())
.bucket(config.getBucketName())
.stream(file.getInputStream(), -1, 10485760).build());
// 路径访问获取
StringBuilder url = new StringBuilder().append(config.getEndpoint())
.append("/").append(config.getBucketName()).append("/").append(objectName);
object.set("height", height);
object.set("width", width);
object.set("size", file.getSize());
object.set("filename", objectName);
object.set("url", url);
} catch (Exception e) {
log.error("++++++++++++文件上传发生异常!++++++++++++++");
log.error(e.getMessage());
}
return object;
}
/**
* 根据指定文件夹上传;通用controller层封装
*/
public BaseResult<Object> uploadByPrefix(MultipartFile file, MinioClient minioClient, MinIoConfig config, String prefix) {
try {
boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder()
.bucket(config.getBucketName()).build());
if (!isExist) {
//创建存储桶
this.createBucket(minioClient, config.getBucketName());
log.info("创建存储桶!");
} else {
log.info("存储桶已经存在!");
// 上传文件
JSONObject object = this.uploadFile(file, minioClient, config, prefix);
return BaseResult.ok("文件上传成功!", object);
}
} catch (Exception e) {
e.printStackTrace();
log.error("上传发生错误: {}!", e.getMessage());
}
return BaseResult.error("文件上传失败!");
}
}其他工具
入库不应该存入域名、查询自动添加域名
此工具适用于处理出入数据库的数据
@Component
public class OssUtil {
@Resource
MinIoConfig ossConfig;
/**
* 给从数据库得到的到的文件列表添加前缀 【用以返回前台】
*/
public List<Oss> addOssDBListPrefix(List<Oss> ossList) {
List<Oss> newOssList = new ArrayList<>();
ossList.forEach(oss -> {
oss.setUrl(ossConfig.getUrlPrefix() + oss.getUrl());
newOssList.add(oss);
});
return newOssList;
}
/**
* 给url添加前缀【用以返回前台】
*/
public String addUrlPrefix(String ossDBUrl) {
ossDBUrl = ossConfig.getUrlPrefix() + ossDBUrl;
return ossDBUrl;
}
/**
* 指定字段名添加前缀
*
* @param data 需要处理的集合
* @param field 需要添加前缀的字段名
* @param <T> 类型
* @return 替换好的集合
*/
public <T> List<T> addAllUrlPrefix(List<T> data, String field) {
data.forEach(each -> {
Field urlField = Optional.ofNullable(ReflectionUtils.findField(each.getClass(), field)).orElseThrow(() -> {
throw new BusinessException("未查询到该字段");
});
ReflectionUtils.makeAccessible(urlField);
ReflectionUtils.setField(urlField, each, ossConfig.getUrlPrefix() + ReflectionUtils.getField(urlField, each));
});
return data;
}
/**
* 替换全部前缀
*
* @param data 需要替换的集合
* @param field 集合里面需要替换url的字段名
* @param <T> 类型
* @return 替换好的集合
*/
public <T> List<T> addAllUrlPrefixPerfect(List<T> data, Function field) {
// data.forEach(each -> {
// Field urlField = Optional.ofNullable(ReflectionUtils.findField(each.getClass(), field)).orElseThrow(() -> {
// throw new BusinessException("未查询到该字段");
// });
// ReflectionUtils.makeAccessible(urlField);
// ReflectionUtils.setField(urlField, each, ossConfig.getUrlPrefix() + ReflectionUtils.getField(urlField, each));
// });
return data;
}
/**
* 给从数据库得到的单个文件添加前缀 【用以返回前台】
*/
public Oss ossDBAddPrefix(Oss oss) {
oss.setUrl(ossConfig.getUrlPrefix() + oss.getUrl());
return oss;
}
/**
* 前台传的oss列表删除前缀,处理成入库数据
*
* @param associationId 关联id
* @param type 文件类型
*/
public List<Oss> delOssListPrefixForInsert(List<Oss> ossList, String associationId, Integer type) {
List<Oss> newOssList = new ArrayList<>();
ossList.forEach(oss -> {
oss.setOssId(IdUtil.getSnowflakeNextIdStr());
oss.setAssociationId(associationId);
if (StringUtils.isNotBlank(oss.getFilename())) {
// 若有文件名,直接保存
oss.setUrl(oss.getFilename());
} else {
// 根据url分割出文件名
oss.setUrl(CharSequenceUtil.removePrefix(oss.getUrl(), ossConfig.getUrlPrefix()));
}
oss.setType(type);
oss.setState(0);
oss.setVersion(1);
oss.setIsDelete(0);
oss.setCreateBy("");
oss.setUpdateBy("");
oss.setCreateTime(LocalDateTime.now());
oss.setUpdateTime(LocalDateTime.now());
newOssList.add(oss);
});
return newOssList;
}
/**
* 前台传的单个oss删除前缀,处理成入库数据
*
* @param associationId 关联id
* @param type 文件类型
*/
public Oss delOssPrefixForInsert(Oss oss, String associationId, Integer type) {
if (StringUtils.isNotBlank(oss.getFilename())) {
// 若有文件名,直接保存
oss.setUrl(oss.getFilename());
} else {
// 根据url分割出文件名
oss.setUrl(CharSequenceUtil.removePrefix(oss.getUrl(), ossConfig.getUrlPrefix()));
}
oss.setType(type);
oss.setAssociationId(associationId);
oss.setCreateBy("");
oss.setUpdateBy("");
return oss;
}
/**
* 给前台传的文件url删除前缀
*/
public String delUrlPrefix(String url) {
url = CharSequenceUtil.removePrefix(url, ossConfig.getUrlPrefix());
return url;
}
}通用文件传输控制器
@Slf4j
@RestController
@RequestMapping("/api/open/oss")
public class UploadController {
@Resource
MinioClient minioClient;
@Resource
MinioUtil minioUtil;
@Resource
MinIoConfig config;
/**
* 上传商品图片
*
* @param file
* @return
*/
@PostMapping("/upload/product")
public BaseResult<Object> uploadObjectProduct(MultipartFile file) {
return minioUtil.uploadByPrefix(file, minioClient, config, "product");
}
/**
* 上传轮播图片
*
* @param file
* @return
*/
@PostMapping("/upload/banner")
public BaseResult<Object> uploadObjectBanner(MultipartFile file) {
return minioUtil.uploadByPrefix(file, minioClient, config, "banner");
}
/**
* 删除文件
*
* @param objectName
* @return
*/
@GetMapping("/delete")
public BaseResult<Object> delObject(@RequestParam("objectName") String objectName) {
try {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(config.getBucketName())
.object(objectName).build());
return BaseResult.ok("文件删除成功!");
} catch (Exception e) {
log.error("文件删除发生错误: {}!", e.getMessage());
e.printStackTrace();
}
return BaseResult.error("文件删除失败!");
}
}
