From 8146465e2ca4b4ae2ccc28c0916c100920415143 Mon Sep 17 00:00:00 2001 From: taozi <9108791@qq.com> Date: Sat, 1 Jul 2023 15:51:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0oss=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=E5=90=84=E7=A7=8Doss=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 3 +- .../main/resources/config/application-dev.yml | 10 + .../resources/config/application-docker.yml | 8 + .../resources/config/application-prod.yml | 9 + .../main/resources/config/application-dev.yml | 9 + .../resources/config/application-docker.yml | 9 +- .../resources/config/application-prod.yml | 8 + .../modules/user/service/dto/PromUserDto.java | 4 +- yshop-oss/pom.xml | 37 +++ .../oss/config/OssAutoConfiguration.java | 27 ++ .../co/yixiang/oss/config/OssProperties.java | 70 +++++ .../co/yixiang/oss/service/OssTemplate.java | 252 ++++++++++++++++++ .../main/resources/META-INF/spring.factories | 2 + yshop-tools/pom.xml | 7 + .../modules/tools/rest/UploadController.java | 32 ++- 15 files changed, 474 insertions(+), 13 deletions(-) create mode 100644 yshop-oss/pom.xml create mode 100644 yshop-oss/src/main/java/co/yixiang/oss/config/OssAutoConfiguration.java create mode 100644 yshop-oss/src/main/java/co/yixiang/oss/config/OssProperties.java create mode 100644 yshop-oss/src/main/java/co/yixiang/oss/service/OssTemplate.java create mode 100644 yshop-oss/src/main/resources/META-INF/spring.factories diff --git a/pom.xml b/pom.xml index 03810e64..12e735ed 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ yshop-mproot yshop-mall yshop-message + yshop-oss YSHOP商城管理系统 @@ -28,7 +29,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.12 + 2.7.13 diff --git a/yshop-admin/src/main/resources/config/application-dev.yml b/yshop-admin/src/main/resources/config/application-dev.yml index f6abea01..4f1ff89d 100644 --- a/yshop-admin/src/main/resources/config/application-dev.yml +++ b/yshop-admin/src/main/resources/config/application-dev.yml @@ -98,3 +98,13 @@ file: avatarMaxSize: 5 +#oss配置 +oss: + endpoint: http://oss-cn-shenzhen.aliyuncs.com + custom-domain: https://xxxxx/%s + access-key: xxxxx + secret-key: xxxxx + bucket-name: xxxxx + defaultName: xxxxx + + diff --git a/yshop-admin/src/main/resources/config/application-docker.yml b/yshop-admin/src/main/resources/config/application-docker.yml index 2e415537..9a603115 100644 --- a/yshop-admin/src/main/resources/config/application-docker.yml +++ b/yshop-admin/src/main/resources/config/application-docker.yml @@ -104,3 +104,11 @@ file: maxSize: 100 avatarMaxSize: 5 +#oss配置 +oss: + endpoint: http://oss-cn-shenzhen.aliyuncs.com + custom-domain: https://xxxxx//%s + access-key: xxxxx + secret-key: xxxxx + bucket-name: xxxxx + defaultName: xxxxx diff --git a/yshop-admin/src/main/resources/config/application-prod.yml b/yshop-admin/src/main/resources/config/application-prod.yml index b4219165..ab021307 100644 --- a/yshop-admin/src/main/resources/config/application-prod.yml +++ b/yshop-admin/src/main/resources/config/application-prod.yml @@ -104,3 +104,12 @@ file: maxSize: 100 avatarMaxSize: 5 +#oss配置 +oss: + endpoint: http://oss-cn-shenzhen.aliyuncs.com + custom-domain: https://xxxxx//%s + access-key: xxxxx + secret-key: xxxxx + bucket-name: xxxxx + defaultName: xxxxx + diff --git a/yshop-app/src/main/resources/config/application-dev.yml b/yshop-app/src/main/resources/config/application-dev.yml index 12ad74ee..f7577c9a 100644 --- a/yshop-app/src/main/resources/config/application-dev.yml +++ b/yshop-app/src/main/resources/config/application-dev.yml @@ -90,4 +90,13 @@ file: avatarMaxSize: 5 +#oss配置 +oss: + endpoint: http://oss-cn-shenzhen.aliyuncs.com + custom-domain: https://xxxxx//%s + access-key: xxxxx + secret-key: xxxxx + bucket-name: xxxxx + defaultName: xxxxx + diff --git a/yshop-app/src/main/resources/config/application-docker.yml b/yshop-app/src/main/resources/config/application-docker.yml index 729df76e..04b2163f 100644 --- a/yshop-app/src/main/resources/config/application-docker.yml +++ b/yshop-app/src/main/resources/config/application-docker.yml @@ -96,4 +96,11 @@ file: maxSize: 100 avatarMaxSize: 5 - +#oss配置 +oss: + endpoint: http://oss-cn-shenzhen.aliyuncs.com + custom-domain: https://xxxxx//%s + access-key: xxxxx + secret-key: xxxxx + bucket-name: xxxxx + defaultName: xxxxx diff --git a/yshop-app/src/main/resources/config/application-prod.yml b/yshop-app/src/main/resources/config/application-prod.yml index fd1f6149..0e3e23f1 100644 --- a/yshop-app/src/main/resources/config/application-prod.yml +++ b/yshop-app/src/main/resources/config/application-prod.yml @@ -97,3 +97,11 @@ file: avatarMaxSize: 5 +#oss配置 +oss: + endpoint: http://oss-cn-shenzhen.aliyuncs.com + custom-domain: https://xxxxx//%s + access-key: xxxxx + secret-key: xxxxx + bucket-name: xxxxx + defaultName: xxxxx diff --git a/yshop-mall/src/main/java/co/yixiang/modules/user/service/dto/PromUserDto.java b/yshop-mall/src/main/java/co/yixiang/modules/user/service/dto/PromUserDto.java index 5b4c691a..110ce594 100644 --- a/yshop-mall/src/main/java/co/yixiang/modules/user/service/dto/PromUserDto.java +++ b/yshop-mall/src/main/java/co/yixiang/modules/user/service/dto/PromUserDto.java @@ -2,6 +2,8 @@ package co.yixiang.modules.user.service.dto; import lombok.Data; +import java.math.BigDecimal; + /** * @ClassName PromUserDto * @Author hupeng <610796224@qq.com> @@ -12,7 +14,7 @@ public class PromUserDto { private String avatar; private String nickname; private Integer childCount; - private Integer numberCount; + private BigDecimal numberCount; private Integer orderCount; private Integer uid; private String time; diff --git a/yshop-oss/pom.xml b/yshop-oss/pom.xml new file mode 100644 index 00000000..a21d544a --- /dev/null +++ b/yshop-oss/pom.xml @@ -0,0 +1,37 @@ + + + + yshop + co.yixiang + 3.3 + + 4.0.0 + + yshop-oss + oss模块 + jar + + 8 + 8 + UTF-8 + + + + com.amazonaws + aws-java-sdk-s3 + 1.11.543 + + + co.yixiang + yshop-common + 2.3 + + + cn.hutool + hutool-all + ${hutool.version} + + + diff --git a/yshop-oss/src/main/java/co/yixiang/oss/config/OssAutoConfiguration.java b/yshop-oss/src/main/java/co/yixiang/oss/config/OssAutoConfiguration.java new file mode 100644 index 00000000..7f64affc --- /dev/null +++ b/yshop-oss/src/main/java/co/yixiang/oss/config/OssAutoConfiguration.java @@ -0,0 +1,27 @@ +package co.yixiang.oss.config; + +import co.yixiang.oss.service.OssTemplate; +import lombok.AllArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * aws 自动配置类 + * + */ +@AllArgsConstructor +@EnableConfigurationProperties({ OssProperties.class }) +public class OssAutoConfiguration { + + private final OssProperties properties; + + @Bean + @ConditionalOnMissingBean(OssTemplate.class) + @ConditionalOnProperty(name = "oss.enable", havingValue = "true", matchIfMissing = true) + public OssTemplate ossTemplate() { + return new OssTemplate(properties); + } + +} diff --git a/yshop-oss/src/main/java/co/yixiang/oss/config/OssProperties.java b/yshop-oss/src/main/java/co/yixiang/oss/config/OssProperties.java new file mode 100644 index 00000000..8d5e8f3b --- /dev/null +++ b/yshop-oss/src/main/java/co/yixiang/oss/config/OssProperties.java @@ -0,0 +1,70 @@ +package co.yixiang.oss.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * aws 配置信息 + * + * @descirption 配置文件添加: oss: enable: true endpoint: http://127.0.0.1:9000 # + * pathStyleAccess 采用nginx反向代理或者AWS S3 配置成true,支持第三方云存储配置成false pathStyleAccess: false + * access-key: bug secret-key: bug bucket-name: bug region: custom-domain: + * https://oss.xxx.com/, + *

+ * bucket 设置公共读权限 + */ +@Data +@ConfigurationProperties(prefix = "oss") +public class OssProperties { + + /** + * 对象存储服务的URL + */ + private String endpoint; + + /** + * 自定义域名 + */ + private String customDomain; + + /** + * true path-style nginx 反向代理和S3默认支持 pathStyle {http://endpoint/bucketname} + * false supports virtual-hosted-style 阿里云等需要配置为 virtual-hosted-style + * 模式{http://bucketname.endpoint} + */ + private Boolean pathStyleAccess = false; + + /** + * 应用ID + */ + private String appId; + + /** + * 区域 + */ + private String region; + + /** + * Access key就像用户ID,可以唯一标识你的账户 + */ + private String accessKey; + + /** + * Secret key是你账户的密码 + */ + private String secretKey; + + /** + * 默认的存储桶名称 + */ + private String bucketName ; + + /** + * 最大线程数,默认: 100 + */ + private Integer maxConnections = 100; + + + private String defaultName ; + +} diff --git a/yshop-oss/src/main/java/co/yixiang/oss/service/OssTemplate.java b/yshop-oss/src/main/java/co/yixiang/oss/service/OssTemplate.java new file mode 100644 index 00000000..d4f32d09 --- /dev/null +++ b/yshop-oss/src/main/java/co/yixiang/oss/service/OssTemplate.java @@ -0,0 +1,252 @@ +package co.yixiang.oss.service; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import com.amazonaws.ClientConfiguration; +import com.amazonaws.Protocol; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.client.builder.AwsClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.*; +import com.amazonaws.util.IOUtils; +import co.yixiang.oss.config.OssProperties; +import lombok.Cleanup; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.beans.factory.InitializingBean; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +/** + * aws-s3 通用存储操作 支持所有兼容s3协议的云存储: {阿里云OSS,腾讯云COS,七牛云,京东云,minio 等} + * + * @date 2023/5/23 6:36 上午 + * @since 1.0 + */ +@RequiredArgsConstructor +public class OssTemplate implements InitializingBean { + + private final OssProperties ossProperties; + private AmazonS3 amazonS3; + + /** + * 创建bucket + * @param bucketName bucket名称 + */ + @SneakyThrows + public void createBucket(String bucketName) { + if (!amazonS3.doesBucketExistV2(bucketName)) { + amazonS3.createBucket((bucketName)); + } + } + + /** + * 获取全部bucket + *

+ * + * @see AWS + * API Documentation + */ + @SneakyThrows + public List getAllBuckets() { + return amazonS3.listBuckets(); + } + + /** + * @param bucketName bucket名称 + * @see AWS + * API Documentation + */ + @SneakyThrows + public Optional getBucket(String bucketName) { + return amazonS3.listBuckets().stream().filter(b -> b.getName().equals(bucketName)).findFirst(); + } + + /** + * @param bucketName bucket名称 + * @see AWS API + * Documentation + */ + @SneakyThrows + public void removeBucket(String bucketName) { + amazonS3.deleteBucket(bucketName); + } + + /** + * 根据文件前置查询文件 + * @param bucketName bucket名称 + * @param prefix 前缀 + * @param recursive 是否递归查询 + * @return S3ObjectSummary 列表 + * @see AWS + * API Documentation + */ + @SneakyThrows + public List getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) { + ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix); + return new ArrayList<>(objectListing.getObjectSummaries()); + } + + /** + * 获取文件外链 + * @param objectName 文件名称 + * @return url + * @see AmazonS3#generatePresignedUrl(String bucketName, String key, Date expiration) + */ + @SneakyThrows + public String getObjectURL(String objectName) { + Date date = new Date(); + DateTime dateTime = DateUtil.offsetMinute(date, 15); + URL url = amazonS3.generatePresignedUrl(ossProperties.getBucketName(), objectName, dateTime.toJdkDate()); + return url.toString(); + } + + /** + * 获取文件 + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @return 二进制流 + * @see AWS + * API Documentation + */ + @SneakyThrows + public S3Object getObject(String bucketName, String objectName) { + return amazonS3.getObject(bucketName, objectName); + } + + + /** + * 内网文件转OSS文件 + * + * @param bucketName bucket名称 + * @param fileName 文件名称 + * @param url url + */ +// public void intranetToOSSUrl(String fileName,String url){ +// putObject(ossProperties.getBucketName(),fileName,HttpDownloader.downloadBytes(url)); +// } + + /** + * 上传文件 + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @param bytes 文件 + * @throws Exception + */ + public void putObject(String bucketName, String objectName, byte[] bytes) { + putObject(bucketName, objectName, bytes, bytes.length, "application/octet-stream"); + } + + /** + * 上传文件 + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @param bytes 文件 + * @param size 大小 + * @param contextType 类型 + * @throws Exception + * @see AWS + * API Documentation + */ + public PutObjectResult putObject(String bucketName, String objectName, byte[] bytes, long size, + String contextType) { + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(size); + objectMetadata.setContentType(contextType); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + // 上传 + return amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata); + + } + + /** + * 上传文件 + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @param stream 文件流 + * @throws Exception + */ + public void putObject(String bucketName, String objectName, InputStream stream) throws Exception { + putObject(bucketName, objectName, stream, (long) stream.available(), "application/octet-stream"); + } + + /** + * 上传文件 + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @param stream 文件流 + * @param size 大小 + * @param contextType 类型 + * @throws Exception + * @see AWS + * API Documentation + */ + public PutObjectResult putObject(String bucketName, String objectName, InputStream stream, long size, + String contextType) throws Exception { + com.amazonaws.services.s3.model.S3Object s3Object = new com.amazonaws.services.s3.model.S3Object(); + // String fileName = getFileName(objectName); + byte[] bytes = IOUtils.toByteArray(stream); + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(size); + objectMetadata.setContentType(contextType); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + // 上传 + return amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata); + + } + + /** + * 获取文件信息 + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @see AWS + * API Documentation + */ + public S3Object getObjectInfo(String bucketName, String objectName) throws Exception { + @Cleanup + S3Object object = amazonS3.getObject(bucketName, objectName); + return object; + } + + /** + * 删除文件 + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @throws Exception + * @see AWS API + * Documentation + */ + public void removeObject(String bucketName, String objectName) throws Exception { + amazonS3.deleteObject(bucketName, objectName); + } + + @Override + public void afterPropertiesSet() { + System.setProperty("com.amazonaws.sdk.disableCertChecking", "true"); + ClientConfiguration clientConfiguration = new ClientConfiguration();//初始化客户端config + clientConfiguration.setMaxConnections(ossProperties.getMaxConnections()); + clientConfiguration.setProtocol(Protocol.HTTPS);//设置mos 请求协议为http + clientConfiguration.setSignerOverride("S3SignerType"); + AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration( + ossProperties.getEndpoint(), ossProperties.getRegion());//创建AWS凭证 + AWSCredentials awsCredentials = new BasicAWSCredentials(ossProperties.getAccessKey(), + ossProperties.getSecretKey()); + AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials); + this.amazonS3 = AmazonS3Client.builder().withEndpointConfiguration(endpointConfiguration) + .withClientConfiguration(clientConfiguration).withCredentials(awsCredentialsProvider) + .disableChunkedEncoding().withPathStyleAccessEnabled(ossProperties.getPathStyleAccess()).build(); + } + +} diff --git a/yshop-oss/src/main/resources/META-INF/spring.factories b/yshop-oss/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..4bd900de --- /dev/null +++ b/yshop-oss/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + co.yixiang.oss.config.OssAutoConfiguration diff --git a/yshop-tools/pom.xml b/yshop-tools/pom.xml index d6c1dac0..0d6c6976 100644 --- a/yshop-tools/pom.xml +++ b/yshop-tools/pom.xml @@ -40,6 +40,13 @@ ${qiniu.version} + + + co.yixiang + yshop-oss + 3.3 + + com.alipay.sdk diff --git a/yshop-tools/src/main/java/co/yixiang/modules/tools/rest/UploadController.java b/yshop-tools/src/main/java/co/yixiang/modules/tools/rest/UploadController.java index 5c504f08..3252100c 100644 --- a/yshop-tools/src/main/java/co/yixiang/modules/tools/rest/UploadController.java +++ b/yshop-tools/src/main/java/co/yixiang/modules/tools/rest/UploadController.java @@ -5,15 +5,17 @@ */ package co.yixiang.modules.tools.rest; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import co.yixiang.api.YshopException; import co.yixiang.constant.ShopConstants; import co.yixiang.constant.SystemConfigConstants; import co.yixiang.enums.ShopCommonEnum; import co.yixiang.modules.tools.service.dto.LocalStorageDto; -import co.yixiang.modules.tools.domain.QiniuContent; import co.yixiang.modules.tools.service.LocalStorageService; -import co.yixiang.modules.tools.service.QiNiuService; +import co.yixiang.oss.config.OssProperties; +import co.yixiang.oss.service.OssTemplate; import co.yixiang.utils.RedisUtils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -41,14 +43,16 @@ import java.util.Map; public class UploadController { private final LocalStorageService localStorageService; - private final QiNiuService qiNiuService; private final RedisUtils redisUtils; + private final OssProperties ossProperties; + private final OssTemplate ossTemplate; - public UploadController(LocalStorageService localStorageService, QiNiuService qiNiuService, - RedisUtils redisUtils) { + public UploadController(LocalStorageService localStorageService, RedisUtils redisUtils, + OssProperties ossProperties, OssTemplate ossTemplate) { this.localStorageService = localStorageService; - this.qiNiuService = qiNiuService; this.redisUtils = redisUtils; + this.ossProperties = ossProperties; + this.ossTemplate = ossTemplate; } @@ -76,13 +80,21 @@ public class UploadController { url = url.append(","+localUrl + "/file/" + localStorageDTO.getType() + "/" + localStorageDTO.getRealName()); } } - } else {//走七牛云 + } else { + // 走oss存储 for (MultipartFile file : files) { - QiniuContent qiniuContent = qiNiuService.upload(file, qiNiuService.find()); + String [] originalFilename = file.getOriginalFilename().split("\\."); + String fileName = ossProperties.getBucketName()+"/file/"+originalFilename[0] + "-" + IdUtil.simpleUUID() + StrUtil.DOT + FileUtil.extName(file.getOriginalFilename()); + try { + ossTemplate.putObject(ossProperties.getBucketName(), fileName, file.getInputStream()); + } catch (Exception e) { + e.printStackTrace(); + } + String fileUrl = String.format(ossProperties.getCustomDomain(), fileName); if ("".equals(url.toString())) { - url = url.append(qiniuContent.getUrl()); + url = url.append(fileUrl); }else{ - url = url.append(","+qiniuContent.getUrl()); + url = url.append(","+fileUrl); } } }