ERP系统图片存储方案-MinIO
背景
文件存储在ERP系统中是不可缺少的功能,erp系统中的附件、商品档案的图片都需要进行存储,现在有很多云服务如阿里的oss可以满足,在这里我们分享基于MinIO自建图片存储方案,这里以 MagicErp为例对MinIO做一个完整的介绍
MinIO简介:
MinIO是基于 Golang 编写的开源免费的对象存储套件,是一款高性能高可用的文件系统服务,可以用来替换FastDFS
MinIO部署
创建文件存储目录
mkdir -p /opt/data/minIO/datadocker
启动命令
docker run -d -p 9000:9000 \
--name minio \
-v /opt/data/minIO/data:/data \
-e "MINIO_ACCESS_KEY=root" \
-e "MINIO_SECRET_KEY=root1234" \
minio/minio server /data
用户名:MINIO_ACCESS_KEY (用户名最低3位)
密码: MINIO_SECRET_KEY (密码最低8位)
详细见 : min.io官网
代码实现
MagicErp提供了多种文件存储方案,如阿里云OSS和MinIO,可一键切换其中一种方案,同时也方便二次开发
文件上传接口类
/**
* 存储方案参数接口
*/
public interface Uploader {
/**
* 配置各个存储方案的参数
* @return 参数列表
*/
List<ConfigItem> definitionConfigItem();
/**
* 上传文件
* @param input 上传对象
* @param scene 业务场景
* @param config 配置信息
* @return
*/
FileVO upload(FileDTO input, String scene, Map config);
/**
* 删除文件
* @param filePath 文件地址
* @param config 配置信息
*/
void deleteFile(String filePath, Map config);
/**
* 获取插件ID
* @return 插件beanId
*/
String getPluginId();
/**
* 生成缩略图路径
* @param url 原图片全路径
* @param width 需要生成图片尺寸的宽
* @param height 需要生成图片尺寸的高
* @return 生成的缩略图路径
*/
String getThumbnailUrl(String url, Integer width, Integer height);
/**
* 存储方案是否开启
* @return 0 不开启 1 开启
*/
Integer getIsOpen();
/**
* 获取插件名称
* @return 插件名称
*/
String getPluginName();
}
minIO的插件类
@Component
public class MinIOPlugin implements Uploader {
/**
* 获取配置参数
*/
@Override
public List<ConfigItem> definitionConfigItem() {
List<ConfigItem> list = new ArrayList();
ConfigItem endpoint = new ConfigItem();
endpoint.setType("text");
endpoint.setName("endpoint");
endpoint.setText("minIO服务地址");
ConfigItem accessKey = new ConfigItem();
accessKey.setType("text");
accessKey.setName("accessKey");
accessKey.setText("用户名");
ConfigItem SecretKey = new ConfigItem();
SecretKey.setType("text");
SecretKey.setName("secretKey");
SecretKey.setText("密钥");
list.add(serviceUrl);
list.add(accessKey);
list.add(SecretKey);
return list;
}
/**
* 获取连接
*/
public MinioClient getClient(Map config, String scene) throws InvalidPortException, InvalidEndpointException, IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException, RegionConflictException, InvalidObjectPrefixException {
String accessKeyId = StringUtil.(config.get("accessKey"));
String accessKeySecret = StringUtil.(config.get("secretKey"));
String endpoint = StringUtil.(config.get("endpoint"));
//进行连接
MinioClient minioClient = new MinioClient(endpoint, accessKeyId, accessKeySecret);
//如果桶为空
if (!minioClient.bucketExists(scene)) {
//创建桶
minioClient.makeBucket(scene);
//创建桶的文件为全部可读
minioClient.setBucketPolicy(scene, "*", PolicyType.READ_ONLY);
}
return minioClient;
}
@Override
public FileVO upload(FileDTO input, String scene, Map config) {
// 获取文件后缀
String ext = input.getExt();
//生成随机图片名
String picName = UUID.randomUUID().().toUpperCase().replace("-", "") + "." + ext;
MinioClient minioClient = null;
try {
//获取连接
minioClient = this.getClient(config, scene);
//文件上传
minioClient.putObject(scene, picName, input.getStream(), input.getStream().available(), "image/" + ext);
} catch (Exception e) {
throw new ServiceException(SystemErrorCode.E802.code(), "上传图片失败");
}
String serviceUrl = StringUtil.(config.get("serviceUrl"));
FileVO file = new FileVO();
file.setName(picName);
file.setExt(ext);
file.setUrl(serviceUrl + "/" + scene + "/" + picName);
return file;
}
@Override
public void deleteFile(String filePath, Map config) {
MinioClient minioClient = null;
String[] split = filePath.split("/");
String bucketName = "";
String objectName = "";
for (int i = 0; i < split.length; i++) {
if (i == 3) {
bucketName = split[i];
}
}
if (bucketName.length() > 3) {
objectName = filePath.substring(filePath.indexOf(bucketName) + bucketName.length() + 1);
} else {
throw new ServiceException(SystemErrorCode.E803.code(), "删除失败,无法解析路径");
}
try {
minioClient = this.getClient(config, bucketName);
minioClient.removeObject(bucketName, objectName);
} catch (Exception e) {
throw new ServiceException(SystemErrorCode.E903.code(),"删除图片失败");
}
}
@Override
public String getPluginId() {
return "minIOPlugin";
}
@Override
public String getThumbnailUrl(String url, Integer width, Integer height) {
// 缩略图全路径
String thumbnailPah = url + "_" + width + "x" + height;
// 返回缩略图全路径
return thumbnailPah;
}
@Override
public Integer getIsOpen() {
return 0;
}
@Override
public String getPluginName() {
return "MinIO存储";
}
}
文件上传入口方法:
/**
* 文件上传<br>
* 接受POST请求<br>
* 同时支持多择文件上传和截图上传
* @param upfile 文件流
* @return
* @throws JSONException
* @throws IOException
*/
@PostMapping(value = "/")
@ApiOperation(value = "ueditor文件/图片上传")
public Map upload( MultipartFile upfile) throws JSONException, IOException {
Map result = new HashMap(16);
if (upfile != null && upfile.getOriginalFilename() != null) {
//文件类型
String contentType= upfile.getContentType();
//获取文件名称后缀
String ext = contentType.substring(contentType.lastIndexOf("/") + 1, contentType.length());
if(!FileUtil.isAllowUpImg(ext)){
result.put("state","不允许上传的文件格式,请上传gif,jpg,png,jpeg,mp4格式文件。");
return result;
}
FileDTO input = new FileDTO();
input.setName(upfile.getOriginalFilename());
input.setStream(upfile.getInputStream());
input.setExt(ext);
FileVO file = this.fileManager.upload(input, "ueditor");
String url = file.getUrl();
String title = file.getName();
String original = file.getName();
result.put("state","SUCCESS");
result.put("url", url);
result.put("title", title);
result.put("name", title);
result.put("original", original);
result.put("type","."+file.getExt());
return result;
}else{
result.put("state","没有读取要上传的文件");
return result;
}
}
}