update 基于S3协议重新实现 OSS模块 支持自定义域名

This commit is contained in:
疯狂的狮子li
2022-05-10 14:48:01 +08:00
parent 6d9a89a65f
commit 3430b090e6
20 changed files with 102 additions and 874 deletions

View File

@@ -23,34 +23,8 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.qiniu</groupId> <groupId>com.amazonaws</groupId>
<artifactId>qiniu-java-sdk</artifactId> <artifactId>aws-java-sdk-s3</artifactId>
<version>${qiniu.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun.oss.version}</version>
</dependency>
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>${qcloud.cos.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -8,31 +8,41 @@ import java.util.List;
* *
* @author Lion Li * @author Lion Li
*/ */
public class OssConstant { public interface OssConstant {
/** /**
* OSS模块KEY * OSS模块KEY
*/ */
public static final String SYS_OSS_KEY = "sys_oss:"; String SYS_OSS_KEY = "sys_oss:";
/** /**
* 对象存储配置KEY * 对象存储配置KEY
*/ */
public static final String OSS_CONFIG_KEY = "OssConfig"; String OSS_CONFIG_KEY = "OssConfig";
/** /**
* 缓存配置KEY * 缓存配置KEY
*/ */
public static final String CACHE_CONFIG_KEY = SYS_OSS_KEY + OSS_CONFIG_KEY; String CACHE_CONFIG_KEY = SYS_OSS_KEY + OSS_CONFIG_KEY;
/** /**
* 预览列表资源开关Key * 预览列表资源开关Key
*/ */
public static final String PEREVIEW_LIST_RESOURCE_KEY = "sys.oss.previewListResource"; String PEREVIEW_LIST_RESOURCE_KEY = "sys.oss.previewListResource";
/** /**
* 系统数据ids * 系统数据ids
*/ */
public static final List<Integer> SYSTEM_DATA_IDS = Arrays.asList(1, 2, 3, 4); List<Integer> SYSTEM_DATA_IDS = Arrays.asList(1, 2, 3, 4);
/**
* 云服务商
*/
String[] CLOUD_SERVICE = new String[] {"aliyun", "qcloud", "qiniu"};
/**
* https 状态
*/
String IS_HTTPS = "Y";
} }

View File

@@ -1,52 +0,0 @@
package com.ruoyi.common.oss.enumd;
import com.ruoyi.common.oss.service.impl.AliyunOssStrategy;
import com.ruoyi.common.oss.service.impl.MinioOssStrategy;
import com.ruoyi.common.oss.service.impl.QcloudOssStrategy;
import com.ruoyi.common.oss.service.impl.QiniuOssStrategy;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 对象存储服务商枚举
*
* @author Lion Li
*/
@Getter
@AllArgsConstructor
public enum OssEnumd {
/**
* 七牛云
*/
QINIU("qiniu", QiniuOssStrategy.class),
/**
* 阿里云
*/
ALIYUN("aliyun", AliyunOssStrategy.class),
/**
* 腾讯云
*/
QCLOUD("qcloud", QcloudOssStrategy.class),
/**
* minio
*/
MINIO("minio", MinioOssStrategy.class);
private final String value;
private final Class<?> beanClass;
public static OssEnumd find(String value) {
for (OssEnumd enumd : values()) {
if (enumd.getValue().equals(value)) {
return enumd;
}
}
return null;
}
}

View File

@@ -1,19 +1,3 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package com.ruoyi.common.oss.enumd; package com.ruoyi.common.oss.enumd;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;

View File

@@ -1,17 +1,17 @@
package com.ruoyi.common.oss.factory; package com.ruoyi.common.oss.factory;
import com.ruoyi.common.core.utils.JsonUtils; import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.oss.constant.OssConstant; import com.ruoyi.common.oss.constant.OssConstant;
import com.ruoyi.common.oss.enumd.OssEnumd; import com.ruoyi.common.oss.core.OssClient;
import com.ruoyi.common.oss.exception.OssException; import com.ruoyi.common.oss.exception.OssException;
import com.ruoyi.common.oss.properties.OssProperties; import com.ruoyi.common.oss.properties.OssProperties;
import com.ruoyi.common.oss.service.IOssStrategy;
import com.ruoyi.common.oss.service.abstractd.AbstractOssStrategy;
import com.ruoyi.common.redis.utils.RedisUtils; import com.ruoyi.common.redis.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* 文件上传Factory * 文件上传Factory
* *
@@ -20,17 +20,19 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class OssFactory { public class OssFactory {
private static final Map<String, OssClient> CLIENT_CACHE = new ConcurrentHashMap<>();
/** /**
* 初始化工厂 * 初始化工厂
*/ */
public static void init() { public static void init() {
log.info("初始化OSS工厂"); log.info("初始化OSS工厂");
RedisUtils.subscribe(OssConstant.CACHE_CONFIG_KEY, String.class, type -> { RedisUtils.subscribe(OssConstant.CACHE_CONFIG_KEY, String.class, configKey -> {
AbstractOssStrategy strategy = getStrategy(type); OssClient client = getClient(configKey);
// 未初始化不处理 // 未初始化不处理
if (strategy.isInit) { if (client != null) {
refresh(type); refresh(configKey);
log.info("订阅刷新OSS配置 => " + type); log.info("订阅刷新OSS配置 => " + configKey);
} }
}); });
} }
@@ -38,42 +40,38 @@ public class OssFactory {
/** /**
* 获取默认实例 * 获取默认实例
*/ */
public static IOssStrategy instance() { public static OssClient instance() {
// 获取redis 默认类型 // 获取redis 默认类型
String type = RedisUtils.getCacheObject(OssConstant.CACHE_CONFIG_KEY); String configKey = RedisUtils.getCacheObject(OssConstant.CACHE_CONFIG_KEY);
if (StringUtils.isEmpty(type)) { if (StringUtils.isEmpty(configKey)) {
throw new OssException("文件存储服务类型无法找到!"); throw new OssException("文件存储服务类型无法找到!");
} }
return instance(type); return instance(configKey);
} }
/** /**
* 根据类型获取实例 * 根据类型获取实例
*/ */
public static IOssStrategy instance(String type) { public static OssClient instance(String configKey) {
OssEnumd enumd = OssEnumd.find(type); OssClient client = getClient(configKey);
if (enumd == null) { if (client == null) {
throw new OssException("文件存储服务类型无法找到!"); refresh(configKey);
return getClient(configKey);
} }
AbstractOssStrategy strategy = getStrategy(type); return client;
if (!strategy.isInit) {
refresh(type);
}
return strategy;
} }
private static void refresh(String type) { private static void refresh(String configKey) {
Object json = RedisUtils.getCacheObject(OssConstant.SYS_OSS_KEY + type); Object json = RedisUtils.getCacheObject(OssConstant.SYS_OSS_KEY + configKey);
OssProperties properties = JsonUtils.parseObject(json.toString(), OssProperties.class); OssProperties properties = JsonUtils.parseObject(json.toString(), OssProperties.class);
if (properties == null) { if (properties == null) {
throw new OssException("系统异常, '" + type + "'配置信息不存在!"); throw new OssException("系统异常, '" + configKey + "'配置信息不存在!");
} }
getStrategy(type).init(properties); CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
} }
private static AbstractOssStrategy getStrategy(String type) { private static OssClient getClient(String configKey) {
OssEnumd enumd = OssEnumd.find(type); return CLIENT_CACHE.get(configKey);
return (AbstractOssStrategy) SpringUtils.getBean(enumd.getBeanClass());
} }
} }

View File

@@ -15,6 +15,11 @@ public class OssProperties {
*/ */
private String endpoint; private String endpoint;
/**
* 自定义域名
*/
private String domain;
/** /**
* 前缀 * 前缀
*/ */

View File

@@ -1,74 +0,0 @@
package com.ruoyi.common.oss.service;
import com.ruoyi.common.oss.entity.UploadResult;
import com.ruoyi.common.oss.enumd.OssEnumd;
import java.io.InputStream;
/**
* 对象存储策略
*
* @author Lion Li
*/
public interface IOssStrategy {
/**
* 创建存储桶
*/
void createBucket();
/**
* 获取服务商类型
* @return 对象存储服务商枚举
*/
OssEnumd getServiceType();
/**
* 文件上传
*
* @param data 文件字节数组
* @param path 文件路径,包含文件名
* @param contentType 文件类型
* @return 返回http地址
*/
UploadResult upload(byte[] data, String path, String contentType);
/**
* 文件删除
*
* @param path 文件路径,包含文件名
*/
void delete(String path);
/**
* 文件上传
*
* @param data 文件字节数组
* @param suffix 后缀
* @param contentType 文件类型
* @return 返回http地址
*/
UploadResult uploadSuffix(byte[] data, String suffix, String contentType);
/**
* 文件上传
*
* @param inputStream 字节流
* @param path 文件路径,包含文件名
* @param contentType 文件类型
* @return 返回http地址
*/
UploadResult upload(InputStream inputStream, String path, String contentType);
/**
* 文件上传
*
* @param inputStream 字节流
* @param suffix 后缀
* @param contentType 文件类型
* @return 返回http地址
*/
UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType);
}

View File

@@ -1,69 +0,0 @@
package com.ruoyi.common.oss.service.abstractd;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.oss.entity.UploadResult;
import com.ruoyi.common.oss.enumd.OssEnumd;
import com.ruoyi.common.oss.properties.OssProperties;
import com.ruoyi.common.oss.service.IOssStrategy;
import java.io.InputStream;
/**
* 对象存储策略(支持七牛、阿里云、腾讯云、minio)
*
* @author Lion Li
*/
public abstract class AbstractOssStrategy implements IOssStrategy {
protected OssProperties properties;
public boolean isInit = false;
public void init(OssProperties properties) {
this.properties = properties;
}
@Override
public abstract void createBucket();
@Override
public abstract OssEnumd getServiceType();
public String getPath(String prefix, String suffix) {
// 生成uuid
String uuid = IdUtil.fastSimpleUUID();
// 文件路径
String path = DateUtils.datePath() + "/" + uuid;
if (StringUtils.isNotBlank(prefix)) {
path = prefix + "/" + path;
}
return path + suffix;
}
@Override
public abstract UploadResult upload(byte[] data, String path, String contentType);
@Override
public abstract void delete(String path);
@Override
public UploadResult upload(InputStream inputStream, String path, String contentType) {
byte[] data = IoUtil.readBytes(inputStream);
return this.upload(data, path, contentType);
}
@Override
public abstract UploadResult uploadSuffix(byte[] data, String suffix, String contentType);
@Override
public abstract UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType);
/**
* 获取域名访问链接
*
* @return 域名访问链接
*/
public abstract String getEndpointLink();
}

View File

@@ -1,115 +0,0 @@
package com.ruoyi.common.oss.service.impl;
import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.oss.entity.UploadResult;
import com.ruoyi.common.oss.enumd.OssEnumd;
import com.ruoyi.common.oss.exception.OssException;
import com.ruoyi.common.oss.properties.OssProperties;
import com.ruoyi.common.oss.service.abstractd.AbstractOssStrategy;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* 阿里云存储策略
*
* @author Lion Li
*/
@Component
public class AliyunOssStrategy extends AbstractOssStrategy {
private OSSClient client;
@Override
public void init(OssProperties ossProperties) {
super.init(ossProperties);
try {
ClientConfiguration configuration = new ClientConfiguration();
DefaultCredentialProvider credentialProvider = new DefaultCredentialProvider(
properties.getAccessKey(), properties.getSecretKey());
client = new OSSClient(properties.getEndpoint(), credentialProvider, configuration);
createBucket();
} catch (Exception e) {
throw new OssException("阿里云存储配置错误! 请检查系统配置:[" + e.getMessage() + "]");
}
isInit = true;
}
@Override
public void createBucket() {
try {
String bucketName = properties.getBucketName();
if (client.doesBucketExist(bucketName)) {
return;
}
CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
client.createBucket(createBucketRequest);
} catch (Exception e) {
throw new OssException("创建Bucket失败, 请核对阿里云配置信息:[" + e.getMessage() + "]");
}
}
@Override
public OssEnumd getServiceType() {
return OssEnumd.ALIYUN;
}
@Override
public UploadResult upload(byte[] data, String path, String contentType) {
return upload(new ByteArrayInputStream(data), path, contentType);
}
@Override
public UploadResult upload(InputStream inputStream, String path, String contentType) {
try {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(contentType);
client.putObject(new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata));
} catch (Exception e) {
throw new OssException("上传文件失败,请检查阿里云配置信息:[" + e.getMessage() + "]");
}
return UploadResult.builder().url(getEndpointLink() + "/" + path).filename(path).build(); }
@Override
public void delete(String path) {
path = path.replace(getEndpointLink() + "/", "");
try {
client.deleteObject(properties.getBucketName(), path);
} catch (Exception e) {
throw new OssException("上传文件失败,请检查阿里云配置信息:[" + e.getMessage() + "]");
}
}
@Override
public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
return upload(data, getPath(properties.getPrefix(), suffix), contentType);
}
@Override
public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
}
@Override
public String getEndpointLink() {
String endpoint = properties.getEndpoint();
StringBuilder sb = new StringBuilder(endpoint);
if (StringUtils.containsAnyIgnoreCase(endpoint, "http://")) {
sb.insert(7, properties.getBucketName() + ".");
} else if (StringUtils.containsAnyIgnoreCase(endpoint, "https://")) {
sb.insert(8, properties.getBucketName() + ".");
} else {
throw new OssException("Endpoint配置错误");
}
return sb.toString();
}
}

View File

@@ -1,186 +0,0 @@
package com.ruoyi.common.oss.service.impl;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.oss.entity.UploadResult;
import com.ruoyi.common.oss.enumd.OssEnumd;
import com.ruoyi.common.oss.enumd.PolicyType;
import com.ruoyi.common.oss.exception.OssException;
import com.ruoyi.common.oss.properties.OssProperties;
import com.ruoyi.common.oss.service.abstractd.AbstractOssStrategy;
import io.minio.*;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* minio存储策略
*
* @author Lion Li
*/
@Component
public class MinioOssStrategy extends AbstractOssStrategy {
private MinioClient minioClient;
@Override
public void init(OssProperties ossProperties) {
super.init(ossProperties);
try {
minioClient = MinioClient.builder()
.endpoint(properties.getEndpoint())
.credentials(properties.getAccessKey(), properties.getSecretKey())
.build();
createBucket();
} catch (Exception e) {
throw new OssException("Minio存储配置错误! 请检查系统配置:[" + e.getMessage() + "]");
}
isInit = true;
}
@Override
public void createBucket() {
try {
String bucketName = properties.getBucketName();
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (exists) {
return;
}
// 不存在就创建桶
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
.bucket(bucketName)
.config(getPolicy(bucketName, PolicyType.READ))
.build());
} catch (Exception e) {
throw new OssException("创建Bucket失败, 请核对Minio配置信息:[" + e.getMessage() + "]");
}
}
@Override
public OssEnumd getServiceType() {
return OssEnumd.MINIO;
}
@Override
public UploadResult upload(byte[] data, String path, String contentType) {
return upload(new ByteArrayInputStream(data), path, contentType);
}
@Override
public UploadResult upload(InputStream inputStream, String path, String contentType) {
try {
// 解决 inputStream.available() 在 socket 下传输延迟问题 导致获取数值不精确
Thread.sleep(1000);
minioClient.putObject(PutObjectArgs.builder()
.bucket(properties.getBucketName())
.object(path)
.contentType(StringUtils.blankToDefault(contentType, MediaType.APPLICATION_OCTET_STREAM_VALUE))
.stream(inputStream, inputStream.available(), -1)
.build());
} catch (Exception e) {
throw new OssException("上传文件失败请核对Minio配置信息:[" + e.getMessage() + "]");
}
return UploadResult.builder().url(getEndpointLink() + "/" + path).filename(path).build();
}
@Override
public void delete(String path) {
path = path.replace(getEndpointLink() + "/", "");
try {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(properties.getBucketName())
.object(path)
.build());
} catch (Exception e) {
throw new OssException(e.getMessage());
}
}
@Override
public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
return upload(data, getPath(properties.getPrefix(), suffix), contentType);
}
@Override
public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
}
@Override
public String getEndpointLink() {
return properties.getEndpoint() + "/" + properties.getBucketName();
}
private String getPolicy(String bucketName, PolicyType policyType) {
StringBuilder builder = new StringBuilder();
builder.append("{\n");
builder.append(" \"Statement\": [\n");
builder.append(" {\n");
builder.append(" \"Action\": [\n");
if (policyType == PolicyType.WRITE) {
builder.append(" \"s3:GetBucketLocation\",\n");
builder.append(" \"s3:ListBucketMultipartUploads\"\n");
} else if (policyType == PolicyType.READ_WRITE) {
builder.append(" \"s3:GetBucketLocation\",\n");
builder.append(" \"s3:ListBucket\",\n");
builder.append(" \"s3:ListBucketMultipartUploads\"\n");
} else {
builder.append(" \"s3:GetBucketLocation\"\n");
}
builder.append(" ],\n");
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": \"*\",\n");
builder.append(" \"Resource\": \"arn:aws:s3:::");
builder.append(bucketName);
builder.append("\"\n");
builder.append(" },\n");
if (PolicyType.READ.equals(policyType)) {
builder.append(" {\n");
builder.append(" \"Action\": [\n");
builder.append(" \"s3:ListBucket\"\n");
builder.append(" ],\n");
builder.append(" \"Effect\": \"Deny\",\n");
builder.append(" \"Principal\": \"*\",\n");
builder.append(" \"Resource\": \"arn:aws:s3:::");
builder.append(bucketName);
builder.append("\"\n");
builder.append(" },\n");
}
builder.append(" {\n");
builder.append(" \"Action\": ");
switch (policyType) {
case WRITE:
builder.append("[\n");
builder.append(" \"s3:AbortMultipartUpload\",\n");
builder.append(" \"s3:DeleteObject\",\n");
builder.append(" \"s3:ListMultipartUploadParts\",\n");
builder.append(" \"s3:PutObject\"\n");
builder.append(" ],\n");
break;
case READ_WRITE:
builder.append("[\n");
builder.append(" \"s3:AbortMultipartUpload\",\n");
builder.append(" \"s3:DeleteObject\",\n");
builder.append(" \"s3:GetObject\",\n");
builder.append(" \"s3:ListMultipartUploadParts\",\n");
builder.append(" \"s3:PutObject\"\n");
builder.append(" ],\n");
break;
default:
builder.append("\"s3:GetObject\",\n");
break;
}
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": \"*\",\n");
builder.append(" \"Resource\": \"arn:aws:s3:::");
builder.append(bucketName);
builder.append("/*\"\n");
builder.append(" }\n");
builder.append(" ],\n");
builder.append(" \"Version\": \"2012-10-17\"\n");
builder.append("}\n");
return builder.toString();
}
}

View File

@@ -1,124 +0,0 @@
package com.ruoyi.common.oss.service.impl;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.oss.entity.UploadResult;
import com.ruoyi.common.oss.enumd.OssEnumd;
import com.ruoyi.common.oss.exception.OssException;
import com.ruoyi.common.oss.properties.OssProperties;
import com.ruoyi.common.oss.service.abstractd.AbstractOssStrategy;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* 腾讯云存储策略
*
* @author Lion Li
*/
@Component
public class QcloudOssStrategy extends AbstractOssStrategy {
private COSClient client;
@Override
public void init(OssProperties ossProperties) {
super.init(ossProperties);
try {
COSCredentials credentials = new BasicCOSCredentials(
properties.getAccessKey(), properties.getSecretKey());
// 初始化客户端配置
ClientConfig clientConfig = new ClientConfig();
// 设置bucket所在的区域华南gz 华北tj 华东sh
clientConfig.setRegion(new Region(properties.getRegion()));
if ("Y".equals(properties.getIsHttps())) {
clientConfig.setHttpProtocol(HttpProtocol.https);
} else {
clientConfig.setHttpProtocol(HttpProtocol.http);
}
client = new COSClient(credentials, clientConfig);
createBucket();
} catch (Exception e) {
throw new OssException("腾讯云存储配置错误! 请检查系统配置:[" + e.getMessage() + "]");
}
isInit = true;
}
@Override
public void createBucket() {
try {
String bucketName = properties.getBucketName();
if (client.doesBucketExist(bucketName)) {
return;
}
CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
createBucketRequest.setCannedAcl(CannedAccessControlList.PublicRead);
client.createBucket(createBucketRequest);
} catch (Exception e) {
throw new OssException("创建Bucket失败, 请核对腾讯云配置信息:[" + e.getMessage() + "]");
}
}
@Override
public OssEnumd getServiceType() {
return OssEnumd.QCLOUD;
}
@Override
public UploadResult upload(byte[] data, String path, String contentType) {
return upload(new ByteArrayInputStream(data), path, contentType);
}
@Override
public UploadResult upload(InputStream inputStream, String path, String contentType) {
try {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(contentType);
client.putObject(new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata));
} catch (Exception e) {
throw new OssException("上传文件失败,请检查腾讯云配置信息:[" + e.getMessage() + "]");
}
return UploadResult.builder().url(getEndpointLink() + "/" + path).filename(path).build();
}
@Override
public void delete(String path) {
path = path.replace(getEndpointLink() + "/", "");
try {
client.deleteObject(new DeleteObjectRequest(properties.getBucketName(), path));
} catch (Exception e) {
throw new OssException("上传文件失败,请检腾讯云查配置信息:[" + e.getMessage() + "]");
}
}
@Override
public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
return upload(data, getPath(properties.getPrefix(), suffix), contentType);
}
@Override
public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
}
@Override
public String getEndpointLink() {
String endpoint = properties.getEndpoint();
StringBuilder sb = new StringBuilder(endpoint);
if (StringUtils.containsAnyIgnoreCase(endpoint, "http://")) {
sb.insert(7, properties.getBucketName() + ".");
} else if (StringUtils.containsAnyIgnoreCase(endpoint, "https://")) {
sb.insert(8, properties.getBucketName() + ".");
} else {
throw new OssException("Endpoint配置错误");
}
return sb.toString();
}
}

View File

@@ -1,127 +0,0 @@
package com.ruoyi.common.oss.service.impl;
import cn.hutool.core.util.ArrayUtil;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import com.ruoyi.common.oss.entity.UploadResult;
import com.ruoyi.common.oss.enumd.OssEnumd;
import com.ruoyi.common.oss.exception.OssException;
import com.ruoyi.common.oss.properties.OssProperties;
import com.ruoyi.common.oss.service.abstractd.AbstractOssStrategy;
import org.springframework.stereotype.Component;
import java.io.InputStream;
/**
* 七牛云存储策略
*
* @author Lion Li
*/
@Component
public class QiniuOssStrategy extends AbstractOssStrategy {
private UploadManager uploadManager;
private BucketManager bucketManager;
private Auth auth;
@Override
public void init(OssProperties ossProperties) {
super.init(ossProperties);
try {
Configuration config = new Configuration(getRegion(properties.getRegion()));
// https设置
config.useHttpsDomains = false;
config.useHttpsDomains = "Y".equals(properties.getIsHttps());
uploadManager = new UploadManager(config);
auth = Auth.create(properties.getAccessKey(), properties.getSecretKey());
bucketManager = new BucketManager(auth, config);
createBucket();
} catch (Exception e) {
throw new OssException("七牛云存储配置错误! 请检查系统配置:[" + e.getMessage() + "]");
}
isInit = true;
}
@Override
public void createBucket() {
try {
String bucketName = properties.getBucketName();
if (ArrayUtil.contains(bucketManager.buckets(), bucketName)) {
return;
}
bucketManager.createBucket(bucketName, properties.getRegion());
} catch (Exception e) {
throw new OssException("创建Bucket失败, 请核对七牛云配置信息:[" + e.getMessage() + "]");
}
}
@Override
public OssEnumd getServiceType() {
return OssEnumd.QINIU;
}
@Override
public UploadResult upload(byte[] data, String path, String contentType) {
try {
String token = auth.uploadToken(properties.getBucketName());
Response res = uploadManager.put(data, path, token, null, contentType, false);
if (!res.isOK()) {
throw new RuntimeException("上传七牛出错:" + res.error);
}
} catch (Exception e) {
throw new OssException("上传文件失败,请核对七牛配置信息:[" + e.getMessage() + "]");
}
return UploadResult.builder().url(getEndpointLink() + "/" + path).filename(path).build();
}
@Override
public void delete(String path) {
try {
path = path.replace(getEndpointLink() + "/", "");
Response res = bucketManager.delete(properties.getBucketName(), path);
if (!res.isOK()) {
throw new RuntimeException("删除七牛文件出错:" + res.error);
}
} catch (Exception e) {
throw new OssException(e.getMessage());
}
}
@Override
public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
return upload(data, getPath(properties.getPrefix(), suffix), contentType);
}
@Override
public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
}
@Override
public String getEndpointLink() {
return properties.getEndpoint();
}
private Region getRegion(String region) {
switch (region) {
case "z0":
return Region.region0();
case "z1":
return Region.region1();
case "z2":
return Region.region2();
case "na0":
return Region.regionNa0();
case "as0":
return Region.regionAs0();
default:
return Region.autoRegion();
}
}
}

View File

@@ -1,5 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.ruoyi.common.oss.service.impl.AliyunOssStrategy,\
com.ruoyi.common.oss.service.impl.MinioOssStrategy,\
com.ruoyi.common.oss.service.impl.QcloudOssStrategy,\
com.ruoyi.common.oss.service.impl.QiniuOssStrategy

View File

@@ -52,6 +52,12 @@ public class SysOssConfig extends BaseEntity {
*/ */
private String endpoint; private String endpoint;
/**
* 自定义域名
*/
private String domain;
/** /**
* 是否https0否 1是 * 是否https0否 1是
*/ */

View File

@@ -35,8 +35,8 @@ public class SysOssConfigBo extends BaseEntity {
/** /**
* 配置key * 配置key
*/ */
@ApiModelProperty(value = "configKey", required = true) @ApiModelProperty(value = "配置key", required = true)
@NotBlank(message = "configKey不能为空", groups = {AddGroup.class, EditGroup.class}) @NotBlank(message = "配置key不能为空", groups = {AddGroup.class, EditGroup.class})
@Size(min = 2, max = 100, message = "configKey长度必须介于2和20 之间") @Size(min = 2, max = 100, message = "configKey长度必须介于2和20 之间")
private String configKey; private String configKey;
@@ -59,8 +59,8 @@ public class SysOssConfigBo extends BaseEntity {
/** /**
* 桶名称 * 桶名称
*/ */
@ApiModelProperty(value = "bucketName", required = true) @ApiModelProperty(value = "桶名称", required = true)
@NotBlank(message = "bucketName不能为空", groups = {AddGroup.class, EditGroup.class}) @NotBlank(message = "桶名称不能为空", groups = {AddGroup.class, EditGroup.class})
@Size(min = 2, max = 100, message = "bucketName长度必须介于2和100之间") @Size(min = 2, max = 100, message = "bucketName长度必须介于2和100之间")
private String bucketName; private String bucketName;
@@ -73,11 +73,17 @@ public class SysOssConfigBo extends BaseEntity {
/** /**
* 访问站点 * 访问站点
*/ */
@ApiModelProperty(value = "endpoint", required = true) @ApiModelProperty(value = "访问站点", required = true)
@NotBlank(message = "endpoint不能为空", groups = {AddGroup.class, EditGroup.class}) @NotBlank(message = "访问站点不能为空", groups = {AddGroup.class, EditGroup.class})
@Size(min = 2, max = 100, message = "endpoint长度必须介于2和100之间") @Size(min = 2, max = 100, message = "endpoint长度必须介于2和100之间")
private String endpoint; private String endpoint;
/**
* 自定义域名
*/
@ApiModelProperty("自定义域名")
private String domain;
/** /**
* 是否httpsY=是,N=否) * 是否httpsY=是,N=否)
*/ */
@@ -93,7 +99,7 @@ public class SysOssConfigBo extends BaseEntity {
/** /**
* 域 * 域
*/ */
@ApiModelProperty(value = "region") @ApiModelProperty(value = "")
private String region; private String region;
/** /**

View File

@@ -60,6 +60,12 @@ public class SysOssConfigVo {
@ApiModelProperty("访问站点") @ApiModelProperty("访问站点")
private String endpoint; private String endpoint;
/**
* 自定义域名
*/
@ApiModelProperty("自定义域名")
private String domain;
/** /**
* 是否httpsY=是,N=否) * 是否httpsY=是,N=否)
*/ */

View File

@@ -2,9 +2,9 @@ package com.ruoyi.resource.dubbo;
import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.oss.core.OssClient;
import com.ruoyi.common.oss.entity.UploadResult; import com.ruoyi.common.oss.entity.UploadResult;
import com.ruoyi.common.oss.factory.OssFactory; import com.ruoyi.common.oss.factory.OssFactory;
import com.ruoyi.common.oss.service.IOssStrategy;
import com.ruoyi.resource.api.RemoteFileService; import com.ruoyi.resource.api.RemoteFileService;
import com.ruoyi.resource.api.domain.SysFile; import com.ruoyi.resource.api.domain.SysFile;
import com.ruoyi.resource.domain.SysOss; import com.ruoyi.resource.domain.SysOss;
@@ -36,7 +36,7 @@ public class RemoteFileServiceImpl implements RemoteFileService {
public SysFile upload(String name, String originalFilename, String contentType, byte[] file) throws ServiceException { public SysFile upload(String name, String originalFilename, String contentType, byte[] file) throws ServiceException {
try { try {
String suffix = StringUtils.substring(originalFilename, originalFilename.lastIndexOf("."), originalFilename.length()); String suffix = StringUtils.substring(originalFilename, originalFilename.lastIndexOf("."), originalFilename.length());
IOssStrategy storage = OssFactory.instance(); OssClient storage = OssFactory.instance();
UploadResult uploadResult = storage.uploadSuffix(file, suffix, contentType); UploadResult uploadResult = storage.uploadSuffix(file, suffix, contentType);
// 保存文件信息 // 保存文件信息
SysOss oss = new SysOss(); SysOss oss = new SysOss();
@@ -44,7 +44,7 @@ public class RemoteFileServiceImpl implements RemoteFileService {
oss.setFileSuffix(suffix); oss.setFileSuffix(suffix);
oss.setFileName(uploadResult.getFilename()); oss.setFileName(uploadResult.getFilename());
oss.setOriginalName(originalFilename); oss.setOriginalName(originalFilename);
oss.setService(storage.getServiceType().getValue()); oss.setService(storage.getConfigKey());
sysOssMapper.insert(oss); sysOssMapper.insert(oss);
SysFile sysFile = new SysFile(); SysFile sysFile = new SysFile();
sysFile.setName(uploadResult.getFilename()); sysFile.setName(uploadResult.getFilename());

View File

@@ -7,9 +7,9 @@ import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.mybatis.core.page.PageQuery; import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo; import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.oss.core.OssClient;
import com.ruoyi.common.oss.entity.UploadResult; import com.ruoyi.common.oss.entity.UploadResult;
import com.ruoyi.common.oss.factory.OssFactory; import com.ruoyi.common.oss.factory.OssFactory;
import com.ruoyi.common.oss.service.IOssStrategy;
import com.ruoyi.resource.domain.SysOss; import com.ruoyi.resource.domain.SysOss;
import com.ruoyi.resource.domain.bo.SysOssBo; import com.ruoyi.resource.domain.bo.SysOssBo;
import com.ruoyi.resource.domain.vo.SysOssVo; import com.ruoyi.resource.domain.vo.SysOssVo;
@@ -65,7 +65,7 @@ public class SysOssServiceImpl implements ISysOssService {
public SysOss upload(MultipartFile file) { public SysOss upload(MultipartFile file) {
String originalfileName = file.getOriginalFilename(); String originalfileName = file.getOriginalFilename();
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length()); String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
IOssStrategy storage = OssFactory.instance(); OssClient storage = OssFactory.instance();
UploadResult uploadResult; UploadResult uploadResult;
try { try {
uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType()); uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
@@ -78,7 +78,7 @@ public class SysOssServiceImpl implements ISysOssService {
oss.setFileSuffix(suffix); oss.setFileSuffix(suffix);
oss.setFileName(uploadResult.getFilename()); oss.setFileName(uploadResult.getFilename());
oss.setOriginalName(originalfileName); oss.setOriginalName(originalfileName);
oss.setService(storage.getServiceType().getValue()); oss.setService(storage.getConfigKey());
baseMapper.insert(oss); baseMapper.insert(oss);
return oss; return oss;
} }
@@ -90,7 +90,7 @@ public class SysOssServiceImpl implements ISysOssService {
} }
List<SysOss> list = baseMapper.selectBatchIds(ids); List<SysOss> list = baseMapper.selectBatchIds(ids);
for (SysOss sysOss : list) { for (SysOss sysOss : list) {
IOssStrategy storage = OssFactory.instance(sysOss.getService()); OssClient storage = OssFactory.instance(sysOss.getService());
storage.delete(sysOss.getUrl()); storage.delete(sysOss.getUrl());
} }
return baseMapper.deleteBatchIds(ids) > 0; return baseMapper.deleteBatchIds(ids) > 0;

View File

@@ -2,14 +2,13 @@
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="配置key" prop="configKey"> <el-form-item label="配置key" prop="configKey">
<el-select v-model="queryParams.configKey" placeholder="请选择配置key" clearable size="small"> <el-input
<el-option v-model="queryParams.configKey"
v-for="configKey in configKeyOptions" placeholder="配置key"
:key="configKey.configKey" clearable
:label="configKey.label" size="small"
:value="configKey.configKey" @keyup.enter.native="handleQuery"
/> />
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="桶名称" prop="bucketName"> <el-form-item label="桶名称" prop="bucketName">
<el-input <el-input
@@ -77,6 +76,7 @@
<el-table-column label="主建" align="center" prop="ossConfigId" v-if="false"/> <el-table-column label="主建" align="center" prop="ossConfigId" v-if="false"/>
<el-table-column label="配置key" align="center" prop="configKey" /> <el-table-column label="配置key" align="center" prop="configKey" />
<el-table-column label="访问站点" align="center" prop="endpoint" width="200" /> <el-table-column label="访问站点" align="center" prop="endpoint" width="200" />
<el-table-column label="自定义域名" align="center" prop="domain" width="200" />
<el-table-column label="桶名称" align="center" prop="bucketName" /> <el-table-column label="桶名称" align="center" prop="bucketName" />
<el-table-column label="前缀" align="center" prop="prefix" /> <el-table-column label="前缀" align="center" prop="prefix" />
<el-table-column label="域" align="center" prop="region" /> <el-table-column label="域" align="center" prop="region" />
@@ -122,18 +122,14 @@
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body> <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="120px"> <el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-form-item label="配置key" prop="configKey"> <el-form-item label="配置key" prop="configKey">
<el-select v-model="form.configKey" placeholder="请选择配置key"> <el-input v-model="form.configKey" placeholder="请输入配置key" />
<el-option
v-for="configKey in configKeyOptions"
:key="configKey.configKey"
:label="configKey.label"
:value="configKey.configKey"
/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="访问站点" prop="endpoint"> <el-form-item label="访问站点" prop="endpoint">
<el-input v-model="form.endpoint" placeholder="请输入访问站点" /> <el-input v-model="form.endpoint" placeholder="请输入访问站点" />
</el-form-item> </el-form-item>
<el-form-item label="自定义域名" prop="domain">
<el-input v-model="form.domain" placeholder="请输入自定义域名" />
</el-form-item>
<el-form-item label="accessKey" prop="accessKey"> <el-form-item label="accessKey" prop="accessKey">
<el-input v-model="form.accessKey" placeholder="请输入accessKey" /> <el-input v-model="form.accessKey" placeholder="请输入accessKey" />
</el-form-item> </el-form-item>
@@ -204,14 +200,6 @@ export default {
total: 0, total: 0,
// 对象存储配置表格数据 // 对象存储配置表格数据
ossConfigList: [], ossConfigList: [],
// configKeyOptions
configKeyOptions: [],
configKeyDatas: [
{ configKey: "minio", label: "Minio" },
{ configKey: "qiniu", label: "七牛云" },
{ configKey: "aliyun", label: "阿里云" },
{ configKey: "qcloud", label: "腾讯云" },
],
// 弹出层标题 // 弹出层标题
title: "", title: "",
// 是否显示弹出层 // 是否显示弹出层
@@ -276,7 +264,6 @@ export default {
}, },
created() { created() {
this.getList(); this.getList();
this.configKeyOptions = this.configKeyDatas;
}, },
methods: { methods: {
/** 查询对象存储配置列表 */ /** 查询对象存储配置列表 */
@@ -303,6 +290,7 @@ export default {
bucketName: undefined, bucketName: undefined,
prefix: undefined, prefix: undefined,
endpoint: undefined, endpoint: undefined,
domain: undefined,
isHttps: "N", isHttps: "N",
region: undefined, region: undefined,
status: "1", status: "1",

View File

@@ -612,6 +612,7 @@ create table sys_oss_config (
bucket_name varchar(255) default '' comment '桶名称', bucket_name varchar(255) default '' comment '桶名称',
prefix varchar(255) default '' comment '前缀', prefix varchar(255) default '' comment '前缀',
endpoint varchar(255) default '' comment '访问站点', endpoint varchar(255) default '' comment '访问站点',
domain varchar(255) default '' comment '自定义域名',
is_https char(1) default 'N' comment '是否httpsY=是,N=否)', is_https char(1) default 'N' comment '是否httpsY=是,N=否)',
region varchar(255) default '' comment '', region varchar(255) default '' comment '',
status char(1) default '1' comment '状态0=正常,1=停用)', status char(1) default '1' comment '状态0=正常,1=停用)',
@@ -624,10 +625,11 @@ create table sys_oss_config (
primary key (oss_config_id) primary key (oss_config_id)
) engine=innodb comment='对象存储配置表'; ) engine=innodb comment='对象存储配置表';
insert into sys_oss_config values (1, 'minio', 'ruoyi', 'ruoyi123', 'ruoyi', '', 'http://localhost:9000', 'N', '', '0', '', 'admin', sysdate(), 'admin', sysdate(), NULL); insert into sys_oss_config values (1, 'minio', 'ruoyi', 'ruoyi123', 'ruoyi', '', 'localhost:9000', '','N', '', '0', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
insert into sys_oss_config values (2, 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 'http://XXX.XXXX.com', 'N', 'z0', '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL); insert into sys_oss_config values (2, 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
insert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 'http://oss-cn-beijing.aliyuncs.com', 'N', '', '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL); insert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
insert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'http://cos.ap-beijing.myqcloud.com', 'N', 'ap-beijing', '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL); insert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
insert into sys_oss_config values (5, 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', 'localhost:9000', '','N', '', '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
-- ---------------------------- -- ----------------------------
-- 18、代码生成业务表 -- 18、代码生成业务表