mirror of
https://github.com/PlayEdu/PlayEdu
synced 2026-02-03 15:22:19 +08:00
minio的sdk更换为s3
This commit is contained in:
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package xyz.playedu.common.config;
|
||||
|
||||
import io.minio.CreateMultipartUploadResponse;
|
||||
import io.minio.ListPartsResponse;
|
||||
import io.minio.MinioAsyncClient;
|
||||
import io.minio.messages.Part;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author 杭州白书科技有限公司
|
||||
*
|
||||
* @create 2023/3/6 16:12
|
||||
*/
|
||||
public class PlayEduMinioClientConfig extends MinioAsyncClient {
|
||||
public PlayEduMinioClientConfig(MinioAsyncClient client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String uploadId(String bucket, String filename) {
|
||||
CreateMultipartUploadResponse response =
|
||||
super.createMultipartUpload(bucket, null, filename, null, null);
|
||||
return response.result().uploadId();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void merge(String bucketName, String objectName, String uploadId) {
|
||||
ListPartsResponse listPartsResponse =
|
||||
super.listParts(bucketName, null, objectName, 10000, 0, uploadId, null, null);
|
||||
List<Part> partList = listPartsResponse.result().partList();
|
||||
Part[] parts = new Part[10000];
|
||||
int partNumber = 1;
|
||||
for (Part part : partList) {
|
||||
parts[partNumber - 1] = new Part(partNumber, part.etag());
|
||||
partNumber++;
|
||||
}
|
||||
super.completeMultipartUpload(bucketName, null, objectName, uploadId, parts, null, null);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import xyz.playedu.common.domain.AppConfig;
|
||||
import xyz.playedu.common.types.LdapConfig;
|
||||
import xyz.playedu.common.types.config.MinioConfig;
|
||||
import xyz.playedu.common.types.config.S3Config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -35,7 +35,7 @@ public interface AppConfigService extends IService<AppConfig> {
|
||||
|
||||
Map<String, String> keyValues();
|
||||
|
||||
MinioConfig getMinioConfig();
|
||||
S3Config getS3Config();
|
||||
|
||||
boolean enabledLdapLogin();
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package xyz.playedu.common.service;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* @Author 杭州白书科技有限公司
|
||||
*
|
||||
* @create 2023/3/7 13:29
|
||||
*/
|
||||
public interface MinioService {
|
||||
|
||||
String url(String path);
|
||||
|
||||
String saveFile(MultipartFile file, String savePath, String contentType);
|
||||
|
||||
String saveBytes(byte[] file, String savePath, String contentType);
|
||||
|
||||
String uploadId(String path);
|
||||
|
||||
String chunkPreSignUrl(String filename, String partNumber, String uploadId);
|
||||
|
||||
String merge(String filename, String uploadId);
|
||||
|
||||
void removeByPath(String path);
|
||||
}
|
||||
@@ -17,6 +17,8 @@ package xyz.playedu.common.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import xyz.playedu.common.constant.ConfigConstant;
|
||||
@@ -25,7 +27,8 @@ import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.mapper.AppConfigMapper;
|
||||
import xyz.playedu.common.service.AppConfigService;
|
||||
import xyz.playedu.common.types.LdapConfig;
|
||||
import xyz.playedu.common.types.config.MinioConfig;
|
||||
import xyz.playedu.common.types.config.S3Config;
|
||||
import xyz.playedu.common.util.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -33,12 +36,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author tengteng
|
||||
* @description 针对表【app_config】的数据库操作Service实现
|
||||
* @createDate 2023-03-09 11:13:33
|
||||
*/
|
||||
@Service
|
||||
@Log4j2
|
||||
public class AppConfigServiceImpl extends ServiceImpl<AppConfigMapper, AppConfig>
|
||||
implements AppConfigService {
|
||||
|
||||
@@ -95,15 +94,30 @@ public class AppConfigServiceImpl extends ServiceImpl<AppConfigMapper, AppConfig
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinioConfig getMinioConfig() {
|
||||
MinioConfig minioConfig = new MinioConfig();
|
||||
public S3Config getS3Config() {
|
||||
S3Config s3Config = new S3Config();
|
||||
Map<String, String> config = keyValues();
|
||||
minioConfig.setAccessKey(config.get(ConfigConstant.MINIO_ACCESS_KEY));
|
||||
minioConfig.setSecretKey(config.get(ConfigConstant.MINIO_SECRET_KEY));
|
||||
minioConfig.setBucket(config.get(ConfigConstant.MINIO_BUCKET));
|
||||
minioConfig.setEndpoint(config.get(ConfigConstant.MINIO_ENDPOINT));
|
||||
minioConfig.setDomain(config.get(ConfigConstant.MINIO_DOMAIN));
|
||||
return minioConfig;
|
||||
s3Config.setAccessKey(config.get(ConfigConstant.MINIO_ACCESS_KEY));
|
||||
s3Config.setSecretKey(config.get(ConfigConstant.MINIO_SECRET_KEY));
|
||||
s3Config.setBucket(config.get(ConfigConstant.MINIO_BUCKET));
|
||||
s3Config.setEndpoint(config.get(ConfigConstant.MINIO_ENDPOINT));
|
||||
s3Config.setRegion(null);
|
||||
s3Config.setService("minio");
|
||||
|
||||
String domain = config.get(ConfigConstant.MINIO_DOMAIN);
|
||||
if (s3Config.getService().equals("minio") && StringUtil.isNotEmpty(domain)) {
|
||||
// 移除 / 后缀
|
||||
if (StringUtil.endsWith(domain, "/")) {
|
||||
domain = domain.substring(0, domain.length() - 1);
|
||||
}
|
||||
// 判断是否携带了bucket
|
||||
if (!StringUtil.endsWith(domain, s3Config.getBucket())) {
|
||||
domain += "/" + s3Config.getBucket();
|
||||
}
|
||||
s3Config.setDomain(domain);
|
||||
}
|
||||
|
||||
return s3Config;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package xyz.playedu.common.service.impl;
|
||||
|
||||
import io.minio.*;
|
||||
import io.minio.http.Method;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import xyz.playedu.common.config.PlayEduMinioClientConfig;
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.service.AppConfigService;
|
||||
import xyz.playedu.common.service.MinioService;
|
||||
import xyz.playedu.common.types.config.MinioConfig;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class MinioServiceImpl implements MinioService {
|
||||
|
||||
@Autowired private AppConfigService appConfigService;
|
||||
|
||||
@SneakyThrows
|
||||
private MinioConfig getMinioConfig() {
|
||||
MinioConfig c = appConfigService.getMinioConfig();
|
||||
if (c.getAccessKey().isBlank()
|
||||
|| c.getSecretKey().isBlank()
|
||||
|| c.getBucket().isBlank()
|
||||
|| c.getDomain().isBlank()
|
||||
|| c.getEndpoint().isBlank()) {
|
||||
throw new ServiceException("MinIO服务未配置");
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private String bucket() {
|
||||
return getMinioConfig().getBucket();
|
||||
}
|
||||
|
||||
public MinioClient getMinioClient() {
|
||||
MinioConfig c = getMinioConfig();
|
||||
|
||||
return MinioClient.builder()
|
||||
.endpoint(c.getEndpoint())
|
||||
.credentials(c.getAccessKey(), c.getSecretKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
public PlayEduMinioClientConfig getPlayEduMinioClient() {
|
||||
MinioConfig c = getMinioConfig();
|
||||
|
||||
MinioAsyncClient client =
|
||||
PlayEduMinioClientConfig.builder()
|
||||
.endpoint(c.getEndpoint())
|
||||
.credentials(c.getAccessKey(), c.getSecretKey())
|
||||
.build();
|
||||
|
||||
return new PlayEduMinioClientConfig(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String url(String path) {
|
||||
MinioConfig c = getMinioConfig();
|
||||
|
||||
return c.getDomain()
|
||||
+ (c.getDomain().endsWith("/") ? "" : "/")
|
||||
+ c.getBucket()
|
||||
+ "/"
|
||||
+ path;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String saveFile(MultipartFile file, String savePath, String contentType) {
|
||||
PutObjectArgs objectArgs =
|
||||
PutObjectArgs.builder().bucket(bucket()).object(savePath).stream(
|
||||
file.getInputStream(), file.getSize(), -1)
|
||||
.contentType(contentType)
|
||||
.build();
|
||||
getMinioClient().putObject(objectArgs);
|
||||
|
||||
return url(savePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadId(String path) {
|
||||
return getPlayEduMinioClient().uploadId(bucket(), path);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String chunkPreSignUrl(String filename, String partNumber, String uploadId) {
|
||||
Map<String, String> extraQueryParams = new HashMap<>();
|
||||
extraQueryParams.put("partNumber", partNumber);
|
||||
extraQueryParams.put("uploadId", uploadId);
|
||||
|
||||
return getMinioClient()
|
||||
.getPresignedObjectUrl(
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.bucket(bucket())
|
||||
.object(filename)
|
||||
.method(Method.PUT)
|
||||
.expiry(60 * 60 * 24)
|
||||
.extraQueryParams(extraQueryParams)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String merge(String filename, String uploadId) {
|
||||
getPlayEduMinioClient().merge(bucket(), filename, uploadId);
|
||||
return url(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeByPath(String path) {
|
||||
getMinioClient()
|
||||
.removeObject(RemoveObjectArgs.builder().bucket(bucket()).object(path).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String saveBytes(byte[] file, String savePath, String contentType) {
|
||||
InputStream inputStream = new ByteArrayInputStream(file);
|
||||
|
||||
PutObjectArgs objectArgs =
|
||||
PutObjectArgs.builder().bucket(bucket()).object(savePath).stream(
|
||||
inputStream, file.length, -1)
|
||||
.contentType(contentType)
|
||||
.build();
|
||||
|
||||
getMinioClient().putObject(objectArgs);
|
||||
|
||||
return url(savePath);
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,12 @@ package xyz.playedu.common.types.config;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MinioConfig {
|
||||
public class S3Config {
|
||||
private String accessKey;
|
||||
private String secretKey;
|
||||
private String bucket;
|
||||
private String endpoint;
|
||||
private String domain;
|
||||
private String region;
|
||||
private String service;
|
||||
}
|
||||
175
playedu-common/src/main/java/xyz/playedu/common/util/S3Util.java
Normal file
175
playedu-common/src/main/java/xyz/playedu/common/util/S3Util.java
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package xyz.playedu.common.util;
|
||||
|
||||
import com.amazonaws.HttpMethod;
|
||||
import com.amazonaws.auth.AWSCredentials;
|
||||
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.AmazonS3ClientBuilder;
|
||||
import com.amazonaws.services.s3.model.*;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.types.config.S3Config;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class S3Util {
|
||||
|
||||
private S3Config defaultConfig;
|
||||
|
||||
public S3Config getS3Config() {
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
public S3Util(S3Config s3Config) {
|
||||
defaultConfig = s3Config;
|
||||
}
|
||||
|
||||
public S3Util setConfig(S3Config config) {
|
||||
defaultConfig = config;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean configIsEmpty() {
|
||||
return defaultConfig == null
|
||||
|| StringUtil.isEmpty(defaultConfig.getDomain())
|
||||
|| StringUtil.isEmpty(defaultConfig.getEndpoint())
|
||||
|| StringUtil.isEmpty(defaultConfig.getAccessKey())
|
||||
|| StringUtil.isEmpty(defaultConfig.getSecretKey());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private AmazonS3 getClient() {
|
||||
if (defaultConfig == null) {
|
||||
throw new ServiceException("存储服务未配置");
|
||||
}
|
||||
AWSCredentials credentials =
|
||||
new BasicAWSCredentials(defaultConfig.getAccessKey(), defaultConfig.getSecretKey());
|
||||
|
||||
AwsClientBuilder.EndpointConfiguration endpointConfiguration =
|
||||
new AwsClientBuilder.EndpointConfiguration(
|
||||
defaultConfig.getEndpoint(), defaultConfig.getRegion());
|
||||
|
||||
return AmazonS3ClientBuilder.standard()
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(endpointConfiguration)
|
||||
.build();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String saveFile(MultipartFile file, String savePath, String contentType) {
|
||||
ObjectMetadata objectMetadata = new ObjectMetadata();
|
||||
objectMetadata.setContentType(contentType);
|
||||
objectMetadata.setContentLength(file.getInputStream().available());
|
||||
getClient()
|
||||
.putObject(
|
||||
defaultConfig.getBucket(), savePath, file.getInputStream(), objectMetadata);
|
||||
return generateEndpointPreSignUrl(savePath);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String saveBytes(byte[] file, String savePath, String contentType) {
|
||||
InputStream inputStream = new ByteArrayInputStream(file);
|
||||
ObjectMetadata objectMetadata = new ObjectMetadata();
|
||||
objectMetadata.setContentType(contentType);
|
||||
objectMetadata.setContentLength(inputStream.available());
|
||||
getClient().putObject(defaultConfig.getBucket(), savePath, inputStream, objectMetadata);
|
||||
return generateEndpointPreSignUrl(savePath);
|
||||
}
|
||||
|
||||
public String uploadId(String path) {
|
||||
InitiateMultipartUploadRequest request =
|
||||
new InitiateMultipartUploadRequest(defaultConfig.getBucket(), path);
|
||||
InitiateMultipartUploadResult result = getClient().initiateMultipartUpload(request);
|
||||
return result.getUploadId();
|
||||
}
|
||||
|
||||
public String generatePartUploadPreSignUrl(
|
||||
String filename, String partNumber, String uploadId) {
|
||||
GeneratePresignedUrlRequest request =
|
||||
new GeneratePresignedUrlRequest(
|
||||
defaultConfig.getBucket(), filename, HttpMethod.PUT);
|
||||
request.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)); // 一个小时有效期
|
||||
request.addRequestParameter("partNumber", partNumber); // 分块索引
|
||||
request.addRequestParameter("uploadId", uploadId); // uploadId
|
||||
return getClient().generatePresignedUrl(request).toString();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String merge(String filename, String uploadId) {
|
||||
AmazonS3 client = getClient();
|
||||
|
||||
ListPartsRequest listPartsRequest =
|
||||
new ListPartsRequest(defaultConfig.getBucket(), filename, uploadId);
|
||||
PartListing parts = client.listParts(listPartsRequest);
|
||||
if (parts.getParts().isEmpty()) {
|
||||
throw new ServiceException("没有已上传的分片文件");
|
||||
}
|
||||
|
||||
List<PartETag> eTags = new ArrayList<>();
|
||||
parts.getParts()
|
||||
.forEach(
|
||||
item -> {
|
||||
eTags.add(new PartETag(item.getPartNumber(), item.getETag()));
|
||||
});
|
||||
|
||||
CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest();
|
||||
request.setBucketName(defaultConfig.getBucket());
|
||||
request.setKey(filename);
|
||||
request.setUploadId(uploadId);
|
||||
request.setPartETags(eTags);
|
||||
|
||||
client.completeMultipartUpload(request);
|
||||
|
||||
return generateEndpointPreSignUrl(filename);
|
||||
}
|
||||
|
||||
public void removeByPath(String path) {
|
||||
DeleteObjectRequest request = new DeleteObjectRequest(defaultConfig.getBucket(), path);
|
||||
getClient().deleteObject(request);
|
||||
}
|
||||
|
||||
public boolean exists(String path) {
|
||||
return getClient().doesObjectExist(defaultConfig.getBucket(), path);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String getContent(String path) {
|
||||
S3Object s3Object = getClient().getObject(defaultConfig.getBucket(), path);
|
||||
return new String(s3Object.getObjectContent().readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public String generateEndpointPreSignUrl(String path) {
|
||||
if (defaultConfig.getService().equals("minio")) {
|
||||
return defaultConfig.getDomain() + "/" + path;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user