兼容MINIO

This commit is contained in:
Chopper 2022-06-07 09:35:58 +08:00
parent 519a608316
commit a22ccbdda5
11 changed files with 320 additions and 60 deletions

View File

@ -1,7 +1,7 @@
package cn.lili.buyer.test.cart;
import cn.lili.modules.file.plugin.FileManagerPlugin;
import cn.lili.modules.file.plugin.FilePlugin;
import cn.lili.modules.goods.entity.dos.Brand;
import cn.lili.modules.goods.service.BrandService;
import com.xkcoding.http.util.StringUtil;
@ -27,7 +27,7 @@ class FileTest {
@Autowired
private FileManagerPlugin fileManagerPlugin;
private FilePlugin fileManagerPlugin;
@Autowired
private BrandService brandService;

View File

@ -12,7 +12,8 @@ import cn.lili.common.utils.Base64DecodeMultipartFile;
import cn.lili.common.utils.CommonUtil;
import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.file.entity.File;
import cn.lili.modules.file.plugin.FileManagerPlugin;
import cn.lili.modules.file.plugin.FilePlugin;
import cn.lili.modules.file.plugin.FilePluginFactory;
import cn.lili.modules.file.service.FileService;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.enums.SettingEnum;
@ -47,7 +48,7 @@ public class UploadController {
@Autowired
private SettingService settingService;
@Autowired
private FileManagerPlugin fileManagerPlugin;
private FilePluginFactory filePluginFactory;
@Autowired
private Cache cache;
@ -86,7 +87,7 @@ public class UploadController {
try {
InputStream inputStream = file.getInputStream();
//上传至第三方云服务或服务器
result = fileManagerPlugin.inputStreamUpload(inputStream, fileKey);
result = filePluginFactory.filePlugin().inputStreamUpload(inputStream, fileKey);
//保存数据信息至数据库
newFile.setName(file.getOriginalFilename());
newFile.setFileSize(file.getSize());

View File

@ -420,6 +420,13 @@
</exclusions>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
</dependencies>

View File

@ -0,0 +1,17 @@
package cn.lili.modules.file.entity.enums;
import com.aliyun.oss.OSS;
/**
* OssEnum
*
* @author Chopper
* @version v1.0
* 2022-06-06 11:23
*/
public enum OssEnum {
/**
*
*/
ALI_OSS, MINIO;
}

View File

@ -1,16 +1,23 @@
package cn.lili.modules.file.plugin;
import cn.lili.modules.file.entity.enums.OssEnum;
import java.io.InputStream;
import java.util.List;
/**
* 文件管理插件
* 文件插件接口
*
* @author Chopper
*/
public interface FileManagerPlugin {
public interface FilePlugin {
/**
* 插件名称
*/
OssEnum pluginName();
/**
* 文件路径上传
*

View File

@ -0,0 +1,59 @@
package cn.lili.modules.file.plugin;
import cn.hutool.json.JSONUtil;
import cn.lili.common.exception.ServiceException;
import cn.lili.modules.file.entity.enums.OssEnum;
import cn.lili.modules.file.plugin.impl.AliFilePlugin;
import cn.lili.modules.file.plugin.impl.MinioFilePlugin;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.OssSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 文件服务抽象工厂 直接返回操作类
*
* @author Chopper
* @version v1.0
* 2022-06-06 11:35
*/
@Component
public class FilePluginFactory {
@Autowired
private SettingService settingService;
/**
* 获取oss client
*
* @return
*/
public FilePlugin filePlugin() {
OssSetting ossSetting = null;
try {
Setting setting = settingService.get(SettingEnum.OSS_SETTING.name());
ossSetting = JSONUtil.toBean(setting.getSettingValue(), OssSetting.class);
switch (OssEnum.valueOf(ossSetting.getType())) {
case MINIO:
return new MinioFilePlugin(ossSetting);
case ALI_OSS:
return new AliFilePlugin(ossSetting);
default:
throw new ServiceException();
}
} catch (Exception e) {
throw new ServiceException();
}
}
}

View File

@ -1,23 +1,17 @@
package cn.lili.modules.file.plugin.impl;
import cn.hutool.core.util.StrUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.modules.file.plugin.FileManagerPlugin;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.file.entity.enums.OssEnum;
import cn.lili.modules.file.plugin.FilePlugin;
import cn.lili.modules.system.entity.dto.OssSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.DeleteObjectsRequest;
import com.aliyun.oss.model.ObjectMetadata;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.InputStream;
@ -29,28 +23,19 @@ import java.util.List;
* @author Chopper
*/
@Component
@Slf4j
public class AliFileManagerPlugin implements FileManagerPlugin {
public class AliFilePlugin implements FilePlugin {
@Autowired
private SettingService settingService;
private OssSetting ossSetting;
/**
* 下一个初始化配置参数的时间
* 这里为了防止多次调用redis减少与redis的交互时间
*/
private static Long nextInitSetting;
public AliFilePlugin(OssSetting ossSetting) {
this.ossSetting = ossSetting;
}
/**
* 暂时设定3分账请求一次设置
*/
private static final Long INTERVAL = 60 * 3 * 1000L;
/**
* 静态设置最快三分钟更新一次
*/
private static OssSetting ossSetting;
@Override
public OssEnum pluginName() {
return OssEnum.ALI_OSS;
}
/**
* 获取oss client
@ -58,32 +43,12 @@ public class AliFileManagerPlugin implements FileManagerPlugin {
* @return
*/
private OSS getOssClient() {
OssSetting ossSetting = getSetting();
return new OSSClientBuilder().build(
ossSetting.getEndPoint(),
ossSetting.getAccessKeyId(),
ossSetting.getAccessKeySecret());
}
/**
* 获取配置
*
* @return
*/
private OssSetting getSetting() {
//如果没有配置或者没有下次刷新时间或者下次刷新时间小于当前时间则从redis 更新一次
if (ossSetting == null || nextInitSetting == null || nextInitSetting < System.currentTimeMillis()) {
Setting setting = settingService.get(SettingEnum.OSS_SETTING.name());
if (setting == null || StrUtil.isBlank(setting.getSettingValue())) {
throw new ServiceException(ResultCode.OSS_NOT_EXIST);
}
nextInitSetting = System.currentTimeMillis() + INTERVAL;
ossSetting = new Gson().fromJson(setting.getSettingValue(), OssSetting.class);
return ossSetting;
}
return ossSetting;
}
/**
* 获取配置前缀
@ -91,7 +56,6 @@ public class AliFileManagerPlugin implements FileManagerPlugin {
* @return
*/
private String getUrlPrefix() {
OssSetting ossSetting = getSetting();
return "https://" + ossSetting.getBucketName() + "." + ossSetting.getEndPoint() + "/";
}
@ -130,7 +94,7 @@ public class AliFileManagerPlugin implements FileManagerPlugin {
try {
ObjectMetadata meta = new ObjectMetadata();
meta.setContentType("image/jpg");
ossClient.putObject(getSetting().getBucketName(), key, inputStream, meta);
ossClient.putObject(ossSetting.getBucketName(), key, inputStream, meta);
} catch (OSSException oe) {
log.error("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
@ -161,7 +125,7 @@ public class AliFileManagerPlugin implements FileManagerPlugin {
try {
ossClient.deleteObjects(
new DeleteObjectsRequest(getSetting().getBucketName()).withKeys(key));
new DeleteObjectsRequest(ossSetting.getBucketName()).withKeys(key));
} catch (OSSException oe) {
log.error("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");

View File

@ -0,0 +1,165 @@
package cn.lili.modules.file.plugin.impl;
import cn.lili.modules.file.entity.enums.OssEnum;
import cn.lili.modules.file.plugin.FilePlugin;
import cn.lili.modules.system.entity.dto.OssSetting;
import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.messages.DeleteObject;
import lombok.extern.slf4j.Slf4j;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
/**
* MINIO文件插件
*
* @author liushuai(liushuai711 @ gmail.com)
* @version v4.0
* @Description:
* @since 2022/6/6 17:45
*/
@Slf4j
public class MinioFilePlugin implements FilePlugin {
private OssSetting ossSetting;
public MinioFilePlugin(OssSetting ossSetting) {
this.ossSetting = ossSetting;
}
/**
* 桶占位符
*/
private static final String BUCKET_PARAM = "${bucket}";
/**
* bucket权限-只读
*/
private static final String READ_ONLY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";
/**
* bucket权限-只读
*/
private static final String WRITE_ONLY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";
/**
* bucket权限-读写
*/
private static final String READ_WRITE = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";
private MinioClient minioClient;
@Override
public OssEnum pluginName() {
return OssEnum.MINIO;
}
@Override
public String pathUpload(String filePath, String key) {
try {
return this.inputStreamUpload(new FileInputStream(filePath), key);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
@Override
public String inputStreamUpload(InputStream inputStream, String key) {
String bucket = "";
try {
MinioClient client = getOssClient();
bucket = ossSetting.getM_bucketName();
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(bucket).stream(inputStream, inputStream.available(), 5 * 1024 * 1024)
.object(key)
.contentType("image/png")
.build();
client.putObject(putObjectArgs);
} catch (ErrorResponseException e) {
e.printStackTrace();
return null;
} catch (Exception e) {
log.error("上传失败2", e);
return null;
}
//拼接出可访问的url地址
return ossSetting.getM_endpoint() + "/" + bucket + "/" + key;
}
@Override
public void deleteFile(List<String> key) {
if (key == null || key.isEmpty()) {
return;
}
MinioClient ossClient = getOssClient();
List<DeleteObject> objectList = key.stream().map(DeleteObject::new).collect(Collectors.toList());
ossClient.removeObjects(RemoveObjectsArgs.builder().objects(objectList).build());
}
/**
* 获取oss client
*
* @return
*/
private MinioClient getOssClient() {
if (minioClient != null) {
return this.minioClient;
}
synchronized (this) {
if (minioClient == null) {
//创建客户端
this.minioClient = MinioClient.builder()
.endpoint(ossSetting.getM_endpoint())
.credentials(ossSetting.getM_accessKey(), ossSetting.getM_secretKey())
.build();
try {
//查看对应的bucket是否已经存在不存在则创建
if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(ossSetting.getM_bucketName()).build())) {
//创建bucket
MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(ossSetting.getM_bucketName()).build();
this.minioClient.makeBucket(makeBucketArgs);
setBucketPolicy(this.minioClient, ossSetting.getM_bucketName(), "read-write");
log.info("创建minio桶成功{}", ossSetting.getM_bucketName());
}
} catch (Exception e) {
e.printStackTrace();
log.error("创建[{}]bucket失败", ossSetting.getM_bucketName());
}
}
}
return minioClient;
}
/**
* 更新桶权限策略
*
* @param bucket
* @param policy 权限
*/
public static void setBucketPolicy(MinioClient client, String bucket, String policy) throws Exception {
switch (policy) {
case "read-only":
client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).config(READ_ONLY.replace(BUCKET_PARAM, bucket)).build());
break;
case "write-only":
client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).config(WRITE_ONLY.replace(BUCKET_PARAM, bucket)).build());
break;
case "read-write":
client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).region("public").config(READ_WRITE.replace(BUCKET_PARAM, bucket)).build());
break;
case "none":
default:
break;
}
}
}

View File

@ -9,7 +9,8 @@ import cn.lili.common.vo.SearchVO;
import cn.lili.modules.file.entity.File;
import cn.lili.modules.file.entity.dto.FileOwnerDTO;
import cn.lili.modules.file.mapper.FileMapper;
import cn.lili.modules.file.plugin.FileManagerPlugin;
import cn.lili.modules.file.plugin.FilePlugin;
import cn.lili.modules.file.plugin.FilePluginFactory;
import cn.lili.modules.file.service.FileService;
import cn.lili.mybatis.util.PageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -31,7 +32,7 @@ import java.util.List;
public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements FileService {
@Autowired
private FileManagerPlugin fileManagerPlugin;
private FilePluginFactory filePluginFactory;
@Override
public void batchDelete(List<String> ids) {
@ -42,7 +43,7 @@ public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements Fi
List<File> files = this.list(queryWrapper);
List<String> keys = new ArrayList<>();
files.forEach(item -> keys.add(item.getFileKey()));
fileManagerPlugin.deleteFile(keys);
filePluginFactory.filePlugin().deleteFile(keys);
this.remove(queryWrapper);
}
@ -68,7 +69,7 @@ public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements Fi
List<File> files = this.list(queryWrapper);
List<String> keys = new ArrayList<>();
files.forEach(item -> keys.add(item.getFileKey()));
fileManagerPlugin.deleteFile(keys);
filePluginFactory.filePlugin().deleteFile(keys);
this.remove(queryWrapper);
}

View File

@ -1,5 +1,7 @@
package cn.lili.modules.system.entity.dto;
import cn.lili.common.utils.StringUtils;
import cn.lili.modules.file.entity.enums.OssEnum;
import lombok.Data;
import java.io.Serializable;
@ -15,6 +17,12 @@ import java.io.Serializable;
public class OssSetting implements Serializable {
private static final long serialVersionUID = 2975271656230801861L;
/**
* oss类型
*/
private String type;
/**
* 域名
*/
@ -35,4 +43,34 @@ public class OssSetting implements Serializable {
* 密钥
*/
private String accessKeySecret = "";
/**
* minio服务地址
*/
private String m_endpoint;
/**
* minio用户名
*/
private String m_accessKey;
/**
* minio密码
*/
private String m_secretKey;
/**
* minio bucket名称
*/
private String m_bucketName;
public String getType() {
//默认给阿里云oss存储类型
if (StringUtils.isEmpty(type)) {
return OssEnum.ALI_OSS.name();
}
return type;
}
}

View File

@ -60,6 +60,7 @@
<spotify>1.2.2</spotify>
<spring-boot-admin>2.3.1</spring-boot-admin>
<owasp-java-html-sanitizer>20211018.2</owasp-java-html-sanitizer>
<minio.version>8.0.3</minio.version>
</properties>
<modules>