This commit is contained in:
clay
2024-03-06 17:44:09 +08:00
commit adaec0eadd
1493 changed files with 219939 additions and 0 deletions

114
common/common-file/pom.xml Normal file
View File

@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>common</artifactId>
<groupId>cn.fateverse</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-file</artifactId>
<properties>
<ftp.version>3.8.0</ftp.version>
<okhttp.version>4.9.3</okhttp.version>
<fastdfs.version>1.27.2</fastdfs.version>
<aliyun.oss.version>3.8.0</aliyun.oss.version>
<huawei.obs.version>3.22.3.1</huawei.obs.version>
<thumbnailator.version>0.4.8</thumbnailator.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--阿里云oss-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun.oss.version}</version>
</dependency>
<!-- 华为云 OBS -->
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>esdk-obs-java</artifactId>
<version>${huawei.obs.version}</version>
</dependency>
<!-- MinIO -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.3</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>2.8.0</version>
</dependency>
<!-- FTP上传文件 -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>${ftp.version}</version>
</dependency>
<!-- FastDFS对象存储 -->
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>${fastdfs.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Common Core 核心依赖 -->
<dependency>
<groupId>cn.fateverse</groupId>
<artifactId>common-core</artifactId>
</dependency>
<!-- Apache io -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
<optional>true</optional>
</dependency>
<!--图片处理工具,处理缩略图-->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>${thumbnailator.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,36 @@
package cn.fateverse.common.file.config;
import cn.fateverse.common.file.service.AliyunFileService;
import cn.fateverse.common.file.service.client.AliyunClient;
import cn.fateverse.common.file.service.client.AliyunClientProvider;
import cn.fateverse.common.file.service.impl.AliyunFileStoreService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* 阿里云oss 自动装配
*
* @author Clay
* @date 2023-02-17
*/
@EnableConfigurationProperties({AliyunProperties.class})
public class AliyunAutoConfiguration {
@Bean
public AliyunFileService aliyunFileService() {
return new AliyunFileService();
}
@Bean
@ConditionalOnMissingBean(AliyunClientProvider.class)
public AliyunClientProvider aliyunClient() {
return new AliyunClient();
}
@Bean("aliyunFileStoreService")
public AliyunFileStoreService aliyunFileStoreService(AliyunFileService aliyunFileService) {
return new AliyunFileStoreService(aliyunFileService);
}
}

View File

@@ -0,0 +1,60 @@
package cn.fateverse.common.file.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 阿里云 配置文件信息
*
* @author Clay
* @date 2023/1/10
*/
@ConfigurationProperties(prefix = "file.store.aliyun")
public class AliyunProperties {
/**
* 地域节点
*/
private String endpoint;
private String accessKeyId;
private String secretAccessKey;
/**
* OSS的Bucket名称
*/
private String bucket;
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public String getBucket() {
return bucket;
}
public void setBucket(String bucket) {
this.bucket = bucket;
}
public String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public String getSecretAccessKey() {
return secretAccessKey;
}
public void setSecretAccessKey(String secretAccessKey) {
this.secretAccessKey = secretAccessKey;
}
}

View File

@@ -0,0 +1,35 @@
package cn.fateverse.common.file.config;
import cn.fateverse.common.file.service.FTPFileService;
import cn.fateverse.common.file.service.client.FTPClientProvider;
import cn.fateverse.common.file.service.impl.FTPFileStoreService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* FTP 自动装配
*
* @author Clay
* @date 2023-03-15
*/
@EnableConfigurationProperties({FTPProperties.class})
public class FTPAutoConfiguration {
@Bean
public FTPFileService ftpFileService() {
return new FTPFileService();
}
@Bean
@ConditionalOnMissingBean(FTPClientProvider.class)
public FTPClientProvider ftpClient() {
return new FTPClientProvider();
}
@Bean("ftpFileStoreService")
public FTPFileStoreService ftpFileStoreService(FTPFileService ftpFileService) {
return new FTPFileStoreService(ftpFileService);
}
}

View File

@@ -0,0 +1,94 @@
package cn.fateverse.common.file.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* FTP 配置信息
*
* @author Clay
* @date 2023-03-15
*/
@ConfigurationProperties(prefix = "file.store.ftp")
public class FTPProperties {
//服务器地址
private String address;
//端口号
private Integer port;
//用户名
private String username;
//密码
private String password;
//字符集编码
private String encoding;
//资源地址
private String asset;
//公开目录
private String pubfiles;
//保护目录
private String prifiles;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEncoding() {
return encoding;
}
public void setEncoding(String encoding) {
this.encoding = encoding;
}
public String getAsset() {
return asset;
}
public void setAsset(String asset) {
this.asset = asset;
}
public String getPubfiles() {
return pubfiles;
}
public void setPubfiles(String pubfiles) {
this.pubfiles = pubfiles;
}
public String getPrifiles() {
return prifiles;
}
public void setPrifiles(String prifiles) {
this.prifiles = prifiles;
}
}

View File

@@ -0,0 +1,21 @@
package cn.fateverse.common.file.config;
import cn.fateverse.common.file.service.impl.FastDFSStoreService;
import com.github.tobato.fastdfs.FdfsClientConfig;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.context.annotation.Import;
import org.springframework.jmx.support.RegistrationPolicy;
@Import(FdfsClientConfig.class)
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
@EnableConfigurationProperties({FastDFSProperties.class})
public class FastDFSAutoConfiguration {
@Bean("fastDFSStoreService")
public FastDFSStoreService fastDFSStoreService(){
return new FastDFSStoreService();
}
}

View File

@@ -0,0 +1,20 @@
package cn.fateverse.common.file.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.ObjectUtils;
@ConfigurationProperties(prefix = "fdfs")
public class FastDFSProperties {
private String bucket;
public String getBucket() {
return ObjectUtils.isEmpty(bucket) ? "group1" : bucket;
}
public void setBucket(String bucket) {
this.bucket = bucket;
}
}

View File

@@ -0,0 +1,9 @@
package cn.fateverse.common.file.config;
/**
* 华为 OBS 配置信息
* @author Clay
* @date 2023-03-17
*/
public class HuaweiOBSProperties {
}

View File

@@ -0,0 +1,37 @@
package cn.fateverse.common.file.config;
import cn.fateverse.common.file.service.MinioFileService;
import cn.fateverse.common.file.service.client.MinIoClient;
import cn.fateverse.common.file.service.client.MinioClientProvider;
import cn.fateverse.common.file.service.impl.MinioFileStoreService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* minio 自动装配
*
* @author Clay
* @date 2023-02-17
*/
@EnableConfigurationProperties({MinioProperties.class})
public class MinioAutoConfiguration {
@Bean
public MinioFileService minioFileService() {
return new MinioFileService();
}
@Bean
@ConditionalOnMissingBean(MinioClientProvider.class)
public MinioClientProvider minioClient() {
return new MinIoClient();
}
@Bean("minioFileStoreService")
public MinioFileStoreService minioFileStoreService(MinioFileService minioFileService) {
return new MinioFileStoreService(minioFileService);
}
}

View File

@@ -0,0 +1,49 @@
package cn.fateverse.common.file.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* minio 配置信息
*
* @author Clay
* @date 2023-02-17
*/
@ConfigurationProperties(prefix = "file.store.minio")
public class MinioProperties {
private String endpoint;
private String bucket;
private String accessKey;
private String secretKey;
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public String getBucket() {
return bucket;
}
public void setBucket(String bucket) {
this.bucket = bucket;
}
public String getAccessKey() {
return accessKey;
}
public void setAccessKey(String accessKey) {
this.accessKey = accessKey;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
}

View File

@@ -0,0 +1,57 @@
package cn.fateverse.common.file.entity;
import lombok.Builder;
import lombok.Data;
/**
* @author Clay
* @date 2023/1/10
*/
@Data
@Builder
public class FileInfo {
/**
* 文件名称
*/
private String fileName;
/**
* 缩略图名称
*/
private String thumbnailFileName;
/**
* 文件原始名称
*/
private String originalFilename;
/**
* 文件url
*/
private String url;
/**
* 资源路径
*/
private String uri;
/**
* 缩略图uri
*/
private String thumbnailUri;
/**
* 路径
*/
private String path;
/**
* 文件类型
*/
private String fileType;
/**
* 文件大小
*/
private Long size;
/**
* 是否是图片
*/
private Boolean isImage;
private String contentType;
}

View File

@@ -0,0 +1,37 @@
package cn.fateverse.common.file.enums;
import cn.fateverse.common.file.service.FileStoreService;
import cn.fateverse.common.file.service.impl.AliyunFileStoreService;
import cn.fateverse.common.file.service.impl.FTPFileStoreService;
import cn.fateverse.common.file.service.impl.MinioFileStoreService;
/**
* @author Clay
* @date 2023-02-16
*/
public enum FTLStoreServiceEnum {
/**
* 阿里云 oss 实现
*/
ALIYUN_OSS(AliyunFileStoreService.class),
/**
* minio 对象存储 实现
*/
MINIO_FILE(MinioFileStoreService.class),
/**
* FTP 文件传输 实现
*/
FTP_FILE(FTPFileStoreService.class),
;
private final Class<? extends FileStoreService> type;
FTLStoreServiceEnum(Class<? extends FileStoreService> type) {
this.type = type;
}
public Class<? extends FileStoreService> getType() {
return type;
}
}

View File

@@ -0,0 +1,231 @@
package cn.fateverse.common.file.service;
import cn.fateverse.common.file.config.AliyunProperties;
import cn.fateverse.common.file.entity.FileInfo;
import cn.fateverse.common.file.service.client.AliyunClientProvider;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.ObjectMetadata;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Clay
* @date 2023-02-17
*/
@Slf4j
public class AliyunFileService {
@Autowired
private AliyunProperties properties;
@Autowired
private AliyunClientProvider clientProvider;
private String endpoint;
private String bucket;
private String accessKeyId;
private String secretAccessKey;
public String getEndpoint() {
return endpoint;
}
public String getBucket() {
return bucket;
}
/**
* 初始化字段信息
*/
@PostConstruct
private void init() {
this.endpoint = properties.getEndpoint();
this.bucket = properties.getBucket();
this.accessKeyId = properties.getAccessKeyId();
this.secretAccessKey = properties.getSecretAccessKey();
}
/**
* 获取 阿里云oss 连接客户端
*
* @return 阿里云oss客户端
*/
private OSSClient connect() {
OSSClient ossClient = clientProvider.getClient(endpoint, accessKeyId, secretAccessKey);
log.debug("Got the client to aliyun oss server {}.", endpoint);
return ossClient;
}
/**
* 创建桶
*
* @param bucketName 桶名
* @return 是否创建成功
*/
public boolean createBucket(String bucketName) {
OSSClient ossClient = connect();
if (!ossClient.doesBucketExist(bucketName)) {
ossClient.createBucket(bucketName);
}
return true;
}
/**
* 删除桶
*
* @param bucketName 桶名
* @return 是否删除成功
*/
public boolean deleteBucket(String bucketName) {
OSSClient ossClient = connect();
if (ossClient.doesBucketExist(bucketName)) {
ossClient.deleteBucket(bucketName);
}
return true;
}
/**
* 文件上传
*
* @param file 文件对象
* @param fileInfo 文件信息
* @return 上传成功后的文件对象
*/
public boolean upload(MultipartFile file, FileInfo fileInfo) {
return upload(bucket, file, fileInfo);
}
/**
* 文件上传
*
* @param file 文件对象
* @param fileInfo 文件信息
* @return 上传成功后的文件对象
*/
public boolean upload(InputStream file, FileInfo fileInfo) {
return upload(bucket, file, fileInfo);
}
/**
* 上传文件
*
* @param bucket 桶
* @param file 文件对象
* @param fileInfo 文件信息
* @return 上传成功后的文件对象
*/
public boolean upload(String bucket, MultipartFile file, FileInfo fileInfo) {
ObjectMetadata meta = null;
if (fileInfo.getIsImage()) {
meta = new ObjectMetadata();
meta.setContentType(file.getContentType());
}
return upload(bucket, file, fileInfo, meta);
}
/**
* 上传文件
*
* @param bucket 桶
* @param file 文件对象
* @param fileInfo 文件信息
* @return 上传成功后的文件对象
*/
public boolean upload(String bucket, InputStream file, FileInfo fileInfo) {
return upload(bucket, file, fileInfo, new ObjectMetadata());
}
/**
* 上传文件
*
* @param bucket 桶
* @param file 文件对象
* @param fileInfo 文件信息
* @param meta 头信息
* @return 上传成功后的文件对象
*/
public boolean upload(String bucket, MultipartFile file, FileInfo fileInfo, ObjectMetadata meta) {
try {
return upload(bucket, file.getInputStream(), fileInfo, meta);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public boolean upload(String bucket, InputStream file, FileInfo fileInfo, ObjectMetadata meta) {
if (bucket == null || bucket.length() <= 0) {
log.error("Bucket name cannot be blank.");
return false;
}
OSSClient ossClient = connect();
checkBucket(ossClient, bucket);
ossClient.putObject(bucket, fileInfo.getUri(), file, meta);
return true;
}
/**
* 删除文件
*
* @param fileName 文件名 (路径+文件名)
* @return 是否删除成功
*/
public boolean delete(String fileName) {
return delete(bucket, fileName);
}
/**
* 删除文件
*
* @param bucket 桶
* @param fileName 文件名 (路径+文件名)
* @return 是否删除成功
*/
public boolean delete(String bucket, String fileName) {
OSSClient ossClient = connect();
ossClient.deleteObject(bucket, fileName);
return true;
}
/**
* 根据文件名获取文件流
*
* @param fileName 文件名 (路径+文件名)
* @return 文件流
*/
public InputStream getStream(String fileName) {
return getStream(bucket, fileName);
}
/**
* 根据文件名获取文件流
*
* @param bucket 桶
* @param fileName 文件名 (路径+文件名)
* @return 文件流
*/
public InputStream getStream(String bucket, String fileName) {
OSSClient ossClient = connect();
return ossClient.getObject(bucket, fileName).getObjectContent();
}
private void checkBucket(OSSClient ossClient, String bucket) {
boolean isExist = ossClient.doesBucketExist(bucket);
if (isExist) {
log.info("Bucket {} already exists.", bucket);
} else {
ossClient.createBucket(bucket);
}
}
}

View File

@@ -0,0 +1,172 @@
package cn.fateverse.common.file.service;
import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.common.file.config.FTPProperties;
import cn.fateverse.common.file.entity.FileInfo;
import cn.fateverse.common.file.utils.FileStoreServiceUtil;
import cn.fateverse.common.file.service.client.FTPClientProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* ftp 文件存储服务
*
* @author Clay
* @date 2023-03-15
*/
@Slf4j
public class FTPFileService {
@Autowired
private FTPProperties properties;
@Autowired
private FTPClientProvider clientProvider;
private String address;
private String asset;
private String pubfiles;
private String prifiles;
public String getAsset() {
return asset;
}
/**
* 初始化字段信息
*/
@PostConstruct
private void init() {
this.asset = properties.getAsset();
this.address = properties.getAddress();
this.pubfiles = properties.getPubfiles();
this.prifiles = properties.getPrifiles();
}
public FTPClient connect() {
FTPClient ftpClient = clientProvider.getClient(address, properties.getPort(),
properties.getUsername(), properties.getPassword(), properties.getEncoding());
log.debug("Got the client to minIO server {}.", address);
return ftpClient;
}
/**
* 上传文件
*
* @param bucket 文件桶 = 公开访问或者非公开文件
* @param file 文件对象
* @param fileInfo 文件信息
* @return 是否上传成功
*/
public boolean upload(String bucket, MultipartFile file, FileInfo fileInfo) {
InputStream inputStream = null;
try {
inputStream = file.getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
return upload(bucket, inputStream, fileInfo);
}
public boolean upload(String bucket, InputStream inputStream, FileInfo fileInfo) {
FTPClient ftpClient = connect();
try {
String destFileName = checkBucket(bucket) + fileInfo.getUri();
List<String> dirList = Arrays.stream(destFileName.split("/")).collect(Collectors.toList());
String fileName = dirList.get(dirList.size() - 1);
dirList = dirList.subList(0, dirList.size() - 1);
for (String dir : dirList) {
if (!ftpClient.changeWorkingDirectory(dir)) {
ftpClient.makeDirectory(dir);
ftpClient.changeWorkingDirectory(dir);
}
}
boolean fileFlag = ftpClient.storeFile(fileName, inputStream);
boolean thumbnailFlag = false;
if (fileInfo.getIsImage() && bucket.equals("pub")) {
byte[] thumbnailBytes = FileStoreServiceUtil.thumbnail(inputStream, fileInfo);
thumbnailFlag = ftpClient.storeFile(fileInfo.getThumbnailFileName(), new ByteArrayInputStream(thumbnailBytes));
}
return fileFlag && thumbnailFlag;
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
ftpClient.logout();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* 删除文件
*
* @param bucket 文件桶 = 公开访问或者非公开文件
* @param destFileName 文件路径+名称
* @return 是否删除成功
*/
public boolean delete(String bucket, String destFileName) {
destFileName = checkBucket(bucket) + destFileName;
FTPClient ftpClient = connect();
try {
return ftpClient.deleteFile(destFileName);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
ftpClient.logout();
} catch (IOException e) {
log.error("error: {}", e.getMessage());
}
}
}
/**
* 获取到文件输入流
*
* @param bucket 文件桶 = 公开访问或者非公开文件
* @param destFileName 文件路径
* @return 文件流
*/
public InputStream getStream(String bucket, String destFileName) {
destFileName = checkBucket(bucket) + destFileName;
FTPClient ftpClient = connect();
try {
return ftpClient.retrieveFileStream(destFileName);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
ftpClient.logout();
} catch (IOException e) {
log.error("error: {}", e.getMessage());
}
}
}
private String checkBucket(String bucket) {
if (bucket.equals("pub")) {
return pubfiles + "/";
} else if (bucket.equals("pri")) {
return prifiles + "/";
} else {
throw new CustomException("ftp文件上传文件错误");
}
}
}

View File

@@ -0,0 +1,126 @@
package cn.fateverse.common.file.service;
import cn.fateverse.common.file.enums.FTLStoreServiceEnum;
import cn.fateverse.common.file.entity.FileInfo;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.List;
/**
* 对象存储组合类,使用责任链模式,可以使用户通过服务类型指定对应的oss服务
*
* @author Clay
* @date 2023-02-16
*/
public class FileStoreCombinationService {
private final List<FileStoreService> fileStoreServiceList;
public FileStoreCombinationService(List<FileStoreService> fileStoreServiceList) {
this.fileStoreServiceList = fileStoreServiceList;
}
/**
* 上传文件
*
* @param file 文件名称
* @param serviceType 服务类型
* @return 上传成功的oss文件对象
*/
public FileInfo upload(MultipartFile file, FTLStoreServiceEnum serviceType) {
for (FileStoreService fileStoreService : fileStoreServiceList) {
if (fileStoreService.getClass().equals(serviceType.getType())) {
return fileStoreService.upload(file);
}
}
return null;
}
/**
* 上传文件
*
* @param file 文件名称
* @param serviceType 服务类型
* @param bucket 文件桶名
* @return 上传成功的oss文件对象
*/
public FileInfo upload(String bucket, MultipartFile file, FTLStoreServiceEnum serviceType) {
for (FileStoreService fileStoreService : fileStoreServiceList) {
if (fileStoreService.getClass().equals(serviceType.getType())) {
return fileStoreService.upload(bucket, file);
}
}
return null;
}
/**
* /**
* 下载文件
*
* @param fileUri 文件的资源定位符
* @param serviceType 服务类型
* @return 文件流
*/
public InputStream download(String fileUri, FTLStoreServiceEnum serviceType) {
for (FileStoreService fileStoreService : fileStoreServiceList) {
if (fileStoreService.getClass().equals(serviceType.getType())) {
return fileStoreService.download(fileUri);
}
}
return null;
}
/**
* 下载文件
*
* @param fileUri 文件的资源定位符
* @param bucket 文件桶名
* @param serviceType 服务类型
* @return 文件流
*/
public InputStream download(String bucket, String fileUri, FTLStoreServiceEnum serviceType) {
for (FileStoreService fileStoreService : fileStoreServiceList) {
if (fileStoreService.getClass().equals(serviceType.getType())) {
return fileStoreService.download(bucket, fileUri);
}
}
return null;
}
/**
* 删除文件
*
* @param fileUri 文件的资源定位符
* @param serviceType 服务类型
* @return 删除状态
*/
public Boolean delete(String fileUri, FTLStoreServiceEnum serviceType) {
for (FileStoreService fileStoreService : fileStoreServiceList) {
if (fileStoreService.getClass().equals(serviceType.getType())) {
return fileStoreService.delete(fileUri);
}
}
return false;
}
/**
* 删除文件
*
* @param fileUri 文件的资源定位符
* @param bucket 文件桶名
* @param serviceType 服务类型
* @return 删除状态
*/
public Boolean delete(String bucket, String fileUri, FTLStoreServiceEnum serviceType) {
for (FileStoreService fileStoreService : fileStoreServiceList) {
if (fileStoreService.getClass().equals(serviceType.getType())) {
return fileStoreService.delete(bucket, fileUri);
}
}
return false;
}
}

View File

@@ -0,0 +1,99 @@
package cn.fateverse.common.file.service;
import cn.fateverse.common.file.entity.FileInfo;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
/**
* 文件存储服务接口类
*
* @author Clay
* @date 2023-02-16
*/
public interface FileStoreService {
/**
* 创建文件桶
*
* @param bucketName 桶名
* @return 创建结果
*/
Boolean createBucket(String bucketName);
/**
* 删除文件桶
*
* @param bucketName 桶名
* @return 删除结果
*/
Boolean deleteBucket(String bucketName);
/**
* 上传文件
*
* @param file 文件对象
* @return FileInfo对象
*/
FileInfo upload(MultipartFile file);
/**
* 上传文件
*
* @param bucket 文件桶名
* @param file 文件对象
* @return FileInfo对象
*/
FileInfo upload(String bucket, MultipartFile file);
/**
* 上传文件
*
* @param file 文件对象
* @return FileInfo对象
*/
FileInfo upload(InputStream file, String fileName);
/**
* 上传文件
*
* @param bucket 文件桶名
* @param file 文件对象
* @return FileInfo对象
*/
FileInfo upload(String bucket, InputStream file, String fileName);
/**
* 下载文件
*
* @param fileUri 文件的资源定位符
* @return 文件流
*/
InputStream download(String fileUri);
/**
* 下载文件
*
* @param bucket 文件桶名
* @param fileUri 文件的资源定位符
* @return 文件流
*/
InputStream download(String bucket, String fileUri);
/**
* 删除文件
*
* @param fileUri 文件的资源定位符
* @return 删除状态
*/
Boolean delete(String fileUri);
/**
* 删除文件
*
* @param bucket 文件桶名
* @param fileUri 文件的资源定位符
* @return 删除状态
*/
Boolean delete(String bucket, String fileUri);
}

View File

@@ -0,0 +1,278 @@
package cn.fateverse.common.file.service;
import cn.fateverse.common.file.service.client.MinioClientProvider;
import cn.fateverse.common.file.config.MinioProperties;
import cn.fateverse.common.file.entity.FileInfo;
import cn.fateverse.common.file.utils.FileStoreServiceUtil;
import io.minio.*;
import io.minio.errors.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
/**
* 使用minio的api实现文件的存储获取等功能
*
* @author Clay
* @date 2023-02-17
*/
@Slf4j
public class MinioFileService {
@Autowired
private MinioProperties properties;
@Autowired
private MinioClientProvider clientProvider;
private String endpoint;
private String bucket;
private String accessKey;
private String secretKey;
public String getEndpoint() {
return endpoint;
}
public String getBucket() {
return bucket;
}
/**
* 初始化字段信息
*/
@PostConstruct
private void init() {
this.endpoint = properties.getEndpoint();
this.bucket = properties.getBucket();
this.accessKey = properties.getAccessKey();
this.secretKey = properties.getSecretKey();
}
/**
* 获取 minio 连接客户端
*
* @return minio客户端
*/
private MinioClient connect() {
MinioClient minioClient = clientProvider.getClient(endpoint, accessKey, secretKey);
log.debug("Got the client to minIO server {}.", endpoint);
return minioClient;
}
/**
* 创建桶
*
* @param bucketName 桶名
* @return 是否创建成功
*/
public boolean createBucket(String bucketName) {
try {
MinioClient minioClient = connect();
if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
return true;
} catch (InvalidResponseException | ErrorResponseException | InsufficientDataException | InternalException |
InvalidKeyException | IOException | NoSuchAlgorithmException | ServerException |
XmlParserException e) {
log.error("error: {}", e.getMessage());
return false;
}
}
/**
* 删除桶
*
* @param bucketName 桶名
* @return 是否删除成功
*/
public boolean deleteBucket(String bucketName) {
try {
MinioClient minioClient = connect();
if (minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}
return true;
} catch (InsufficientDataException | ErrorResponseException | InternalException | InvalidKeyException |
InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException |
XmlParserException e) {
log.error("error: {}", e.getMessage());
return false;
}
}
/**
* 文件上传
*
* @param file 文件对象
* @param fileInfo 文件信息
* @return 是否上传成功
*/
public boolean upload(MultipartFile file, FileInfo fileInfo) {
return upload(bucket, file, fileInfo);
}
/**
* 上传文件
*
* @param bucket 桶
* @param file 文件对象
* @param fileInfo 文件信息
* @return 是否上传成功
*/
public boolean upload(String bucket, MultipartFile file, FileInfo fileInfo) {
Map<String, String> headers = new HashMap<>();
if (fileInfo.getIsImage()) {
headers.put("Content-Type", "image/jpg");
}
return upload(bucket, file, fileInfo, headers);
}
/**
* 文件上传
*
* @param file 文件对象
* @param fileInfo 文件信息
* @return 是否上传成功
*/
public boolean upload(InputStream file, FileInfo fileInfo) {
return upload(bucket, file, fileInfo);
}
/**
* 上传文件
*
* @param bucket 桶
* @param file 文件对象
* @param fileInfo 文件信息
* @return 是否上传成功
*/
public boolean upload(String bucket, InputStream file, FileInfo fileInfo) {
return upload(bucket, file, fileInfo, new HashMap<>());
}
public boolean upload(String bucket, MultipartFile file, FileInfo fileInfo, Map<String, String> headers) {
try {
return upload(bucket, file.getInputStream(), fileInfo, headers);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* @param bucket 桶
* @param file 文件对象
* @param fileInfo 文件信息
* @param headers 头部信息
* @return 是否上传成功
*/
public boolean upload(String bucket, InputStream file, FileInfo fileInfo, Map<String, String> headers) {
if (bucket == null || bucket.length() <= 0) {
log.error("Bucket name cannot be blank.");
return false;
}
try {
MinioClient minioClient = connect();
checkBucket(minioClient, bucket);
minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(fileInfo.getUri())
.contentType(headers.get("Content-Type"))
.headers(headers).stream(file, file.available(), -1).build());
if (fileInfo.getIsImage()) {
byte[] thumbnailBytes = FileStoreServiceUtil.thumbnail(file, fileInfo);
minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(fileInfo.getPath() + "/" + fileInfo.getThumbnailFileName())
.stream(new ByteArrayInputStream(thumbnailBytes), thumbnailBytes.length, -1)
.contentType(headers.get("Content-Type"))
.headers(headers).build());
}
return true;
} catch (ServerException | InsufficientDataException | ErrorResponseException | NoSuchAlgorithmException |
IOException | InvalidKeyException | InvalidResponseException | XmlParserException |
InternalException e) {
log.error("error: {}", e.getMessage());
return false;
}
}
/**
* 删除文件
*
* @param fileName 文件名 (路径+文件名)
* @return 是否删除成功
*/
public boolean delete(String fileName) {
return delete(bucket, fileName);
}
/**
* 删除文件
*
* @param bucket 桶
* @param fileName 文件名 (路径+文件名)
* @return 是否删除成功
*/
public boolean delete(String bucket, String fileName) {
try {
MinioClient minioClient = connect();
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(fileName).build());
return true;
} catch (InvalidResponseException | ErrorResponseException | InsufficientDataException | InternalException |
InvalidKeyException | IOException | NoSuchAlgorithmException | ServerException |
XmlParserException e) {
log.error("error: {}", e.getMessage());
return false;
}
}
/**
* 根据文件名获取文件流
*
* @param fileName 文件名 (路径+文件名)
* @return 文件流
*/
public InputStream getStream(String fileName) {
return getStream(bucket, fileName);
}
/**
* 根据文件名获取文件流
*
* @param bucket 桶
* @param fileName 文件名 (路径+文件名)
* @return 文件流
*/
public InputStream getStream(String bucket, String fileName) {
try {
MinioClient minioClient = connect();
return minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(fileName).build());
} catch (InvalidResponseException | ErrorResponseException | InsufficientDataException | InternalException |
InvalidKeyException | IOException | NoSuchAlgorithmException | ServerException |
XmlParserException e) {
log.error("error: {}", e.getMessage());
return null;
}
}
private void checkBucket(MinioClient minioClient, String bucket) throws ServerException, InsufficientDataException,
ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException,
XmlParserException, InternalException {
boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
if (isExist) {
log.info("Bucket {} already exists.", bucket);
} else {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
}
}
}

View File

@@ -0,0 +1,35 @@
package cn.fateverse.common.file.service.client;
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
/**
* @author Clay
* @date 2023-02-17
*/
public class AliyunClient implements AliyunClientProvider{
private volatile OSSClient ossClient = null;
@Override
public OSSClient getClient(String endpoint, String accessKeyId, String secretAccessKey) {
if (ossClient == null){
synchronized (AliyunClient.class){
if (ossClient == null){
ossClient = new OSSClient(endpoint,getDefaultCredentialProvider(accessKeyId,secretAccessKey),getClientConfiguration());
}
}
}
return ossClient;
}
private static DefaultCredentialProvider getDefaultCredentialProvider(String accessKeyId, String secretAccessKey) {
return new DefaultCredentialProvider(accessKeyId, secretAccessKey);
}
private static ClientBuilderConfiguration getClientConfiguration() {
return new ClientBuilderConfiguration();
}
}

View File

@@ -0,0 +1,20 @@
package cn.fateverse.common.file.service.client;
import com.aliyun.oss.OSSClient;
/**
* 阿里云 客户端 provider
* @author Clay
* @date 2023-02-17
*/
public interface AliyunClientProvider {
/**
* 获取一个阿里云iss
* @param endpoint 端点
* @param accessKey accessKey
* @param secretKey secretKey
* @return OSS
*/
OSSClient getClient(String endpoint, String accessKey, String secretKey);
}

View File

@@ -0,0 +1,58 @@
package cn.fateverse.common.file.service.client;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ftp 客户端 provider
*
* @author Clay
* @date 2023-03-15
*/
public class FTPClientProvider {
private static final Logger logger = LoggerFactory.getLogger(FTPClientProvider.class);
/**
* 获取到ftp客户端
*
* @param address 地址
* @param port 端口
* @param username 用户名
* @param password 密码
* @param encoding 字符集编码
* @return ftp客户端
*/
public FTPClient getClient(String address, Integer port, String username, String password, String encoding) {
//实例FTP客户端
FTPClient ftpClient = new FTPClient();
ftpClient.setAutodetectUTF8(true);
try {
ftpClient.connect(address, port);
ftpClient.enterLocalPassiveMode();
// 保存FTP控制连接使用的字符集必须在连接前设置
ftpClient.login(username, password, encoding);
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
//设置linux ftp服务器
FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
ftpClient.configure(conf);
if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
logger.error("未连接到FTP用户名或密码错误。");
ftpClient.disconnect();
throw new RuntimeException("未连接到FTP用户名或密码错误。");
} else {
logger.info("FTP连接成功");
return ftpClient;
}
} catch (Exception e) {
throw new RuntimeException("FTP登录失败");
}
}
}

View File

@@ -0,0 +1,27 @@
package cn.fateverse.common.file.service.client;
import io.minio.MinioClient;
import lombok.extern.slf4j.Slf4j;
/**
* @author Clay
* @date 2023-02-17
*/
@Slf4j
public class MinIoClient implements MinioClientProvider {
private volatile MinioClient minioClient = null;
@Override
public MinioClient getClient(String endpoint, String accessKey, String secretKey) {
if (null == minioClient) {
synchronized (MinIoClient.class) {
if (null == minioClient) {
minioClient = new MinioClient.Builder().credentials(accessKey, secretKey).endpoint(endpoint).build();
}
}
}
return minioClient;
}
}

View File

@@ -0,0 +1,21 @@
package cn.fateverse.common.file.service.client;
import io.minio.MinioClient;
/**
* minIO 客户端 provider
* @author Clay
* @date 2023-02-17
*/
public interface MinioClientProvider {
/**
* 获取一个MinioClient
* @param endpoint 端点
* @param accessKey accessKey
* @param secretKey secretKey
* @return MinioClient
*/
MinioClient getClient(String endpoint, String accessKey, String secretKey);
}

View File

@@ -0,0 +1,103 @@
package cn.fateverse.common.file.service.impl;
import cn.fateverse.common.file.entity.FileInfo;
import cn.fateverse.common.file.service.AliyunFileService;
import cn.fateverse.common.file.service.FileStoreService;
import cn.fateverse.common.file.utils.FileStoreServiceUtil;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
/**
* aliyun 对象存储实现类
*
* @author Clay
* @date 2023/1/10
*/
public class AliyunFileStoreService implements FileStoreService {
private final AliyunFileService aliyunFileService;
private final String bucket;
public AliyunFileStoreService(AliyunFileService aliyunFileService) {
this.aliyunFileService = aliyunFileService;
this.bucket = aliyunFileService.getBucket();
}
@Override
public Boolean createBucket(String bucketName) {
return aliyunFileService.createBucket(bucketName);
}
@Override
public Boolean deleteBucket(String bucketName) {
return aliyunFileService.deleteBucket(bucketName);
}
public FileInfo upload(MultipartFile file) {
// 获取oss的Bucket名称
return upload(bucket, file);
}
@Override
public FileInfo upload(String bucket, MultipartFile file) {
// 获取oss目标文件夹
FileInfo fileInfo = FileStoreServiceUtil.getOssFile(file);
String uri = fileInfo.getUri();
aliyunFileService.upload(bucket, file, fileInfo);
String url = "https://" + bucket + "." + aliyunFileService.getEndpoint() + "/" + uri;
fileInfo.setUrl(url);
if (fileInfo.getIsImage()) {
fileInfo.setThumbnailUri(fileInfo.getUri() + "?x-oss-process=image/resize,m_lfit,h_200,w_200");
fileInfo.setThumbnailFileName(null);
}
return fileInfo;
}
@Override
public FileInfo upload(InputStream file, String fileName) {
return upload(bucket, file, fileName);
}
@Override
public FileInfo upload(String bucket, InputStream file, String fileName) {
// 获取oss目标文件夹
FileInfo fileInfo = FileStoreServiceUtil.getFileInfo(file, fileName);
String uri = fileInfo.getUri();
aliyunFileService.upload(bucket, file, fileInfo);
String url = "https://" + bucket + "." + aliyunFileService.getEndpoint() + "/" + uri;
fileInfo.setUrl(url);
if (fileInfo.getIsImage()) {
fileInfo.setThumbnailUri(fileInfo.getUri() + "?x-oss-process=image/resize,m_lfit,h_200,w_200");
fileInfo.setThumbnailFileName(null);
}
return fileInfo;
}
public InputStream download(String fileUri) {
// 获取oss的Bucket名称
return download(bucket, fileUri);
}
@Override
public InputStream download(String bucket, String fileUri) {
// ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
return aliyunFileService.getStream(bucket, fileUri);
}
public Boolean delete(String fileUri) {
// 获取oss的Bucket名称
return delete(bucket, fileUri);
}
@Override
public Boolean delete(String bucket, String fileUri) {
aliyunFileService.delete(bucket, fileUri);
return Boolean.TRUE;
}
}

View File

@@ -0,0 +1,95 @@
package cn.fateverse.common.file.service.impl;
import cn.fateverse.common.file.entity.FileInfo;
import cn.fateverse.common.file.service.FTPFileService;
import cn.fateverse.common.file.service.FileStoreService;
import cn.fateverse.common.file.utils.FileStoreServiceUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
/**
* ftp文件上传
*
* @author Clay
* @date 2023-03-15
*/
@Slf4j
public class FTPFileStoreService implements FileStoreService {
private final FTPFileService ftpFileService;
private final String asset;
private final String bucket = "pub";
public FTPFileStoreService(FTPFileService ftpFileService) {
this.ftpFileService = ftpFileService;
this.asset = ftpFileService.getAsset();
}
@Override
public Boolean createBucket(String bucketName) {
return null;
}
@Override
public Boolean deleteBucket(String bucketName) {
return null;
}
@Override
public FileInfo upload(MultipartFile file) {
return upload(bucket, file);
}
@Override
public FileInfo upload(String bucket, MultipartFile file) {
FileInfo fileInfo = FileStoreServiceUtil.getOssFile(file);
ftpFileService.upload(bucket, file, fileInfo);
if (bucket.equals("pub")) {
String url = asset + "/" + fileInfo.getUri();
fileInfo.setUrl(url);
}
return fileInfo;
}
@Override
public FileInfo upload(InputStream file, String fileName) {
return upload(bucket, file, fileName);
}
@Override
public FileInfo upload(String bucket, InputStream file, String fileName) {
FileInfo fileInfo = FileStoreServiceUtil.getFileInfo(file, fileName);
ftpFileService.upload(bucket, file, fileInfo);
if (bucket.equals("pub")) {
String url = asset + "/" + fileInfo.getUri();
fileInfo.setUrl(url);
}
return fileInfo;
}
@Override
public InputStream download(String fileUri) {
// 获取oss的Bucket名称
return ftpFileService.getStream(bucket, fileUri);
}
@Override
public InputStream download(String bucket, String fileUri) {
return download(fileUri);
}
@Override
public Boolean delete(String fileUri) {
return delete(bucket, fileUri);
}
@Override
public Boolean delete(String bucket, String fileUri) {
return ftpFileService.delete(bucket, fileUri);
}
}

View File

@@ -0,0 +1,150 @@
package cn.fateverse.common.file.service.impl;
import cn.fateverse.common.file.config.FastDFSProperties;
import cn.fateverse.common.file.entity.FileInfo;
import cn.fateverse.common.file.service.FileStoreService;
import cn.fateverse.common.file.utils.FileStoreServiceUtil;
import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.upload.FastFile;
import com.github.tobato.fastdfs.domain.upload.FastImageFile;
import com.github.tobato.fastdfs.domain.upload.ThumbImage;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
@Slf4j
public class FastDFSStoreService implements FileStoreService {
@Resource
private FastDFSProperties properties;
@Resource
private FastFileStorageClient storageClient;
@Resource
private FdfsWebServer fdfsWebServer;
@Override
public Boolean createBucket(String bucketName) {
return false;
}
@Override
public Boolean deleteBucket(String bucketName) {
return false;
}
@Override
public FileInfo upload(MultipartFile file) {
FileInfo fileInfo = FileStoreServiceUtil.getOssFile(file);
try {
return upload(file.getInputStream(), fileInfo, properties.getBucket());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public FileInfo upload(String bucket, MultipartFile file) {
FileInfo fileInfo = FileStoreServiceUtil.getOssFile(file);
try {
return upload(file.getInputStream(), fileInfo, bucket);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public FileInfo upload(InputStream file, String fileName) {
FileInfo fileInfo = FileStoreServiceUtil.getFileInfo(file, fileName);
return upload(file, fileInfo, properties.getBucket());
}
@Override
public FileInfo upload(String bucket, InputStream file, String fileName) {
FileInfo fileInfo = FileStoreServiceUtil.getFileInfo(file, fileName);
return upload(file, fileInfo, bucket);
}
private FileInfo upload(InputStream file, FileInfo fileInfo, String bucket) {
try {
StorePath storePath;
if (fileInfo.getIsImage()) {
FastImageFile fastImageFile = new FastImageFile.Builder()
.toGroup(bucket)
.withThumbImage()
.withFile(file, fileInfo.getSize(), fileInfo.getFileType())
.build();
storePath = storageClient.uploadImage(fastImageFile);
setPath(fileInfo, storePath);
ThumbImage thumbImage = fastImageFile.getThumbImage();
String prefixName = thumbImage.getPrefixName();
String fileName = fileInfo.getFileName().substring(0, fileInfo.getFileName().lastIndexOf("."));
String thumbnailFileName = fileName + prefixName + "." + fileInfo.getFileType();
fileInfo.setThumbnailFileName(thumbnailFileName);
String thumbnailUri = fdfsWebServer.getWebServerUrl() + "/" + properties.getBucket() + "/" + fileInfo.getPath() + "/" + thumbnailFileName;
fileInfo.setThumbnailUri(thumbnailUri);
} else {
FastFile fastFile = new FastFile.Builder()
.toGroup(bucket)
.withFile(file, fileInfo.getSize(), fileInfo.getFileType())
.build();
storePath = storageClient.uploadFile(fastFile);
setPath(fileInfo, storePath);
}
return fileInfo;
} catch (Exception e) {
log.error("文件上传失败", e);
throw new RuntimeException("文件上传失败");
}
}
private void setPath(FileInfo fileInfo, StorePath storePath) {
String path = storePath.getPath();
String[] split = path.split("/");
String fileName = split[split.length - 1];
fileInfo.setUri(path);
fileInfo.setFileName(fileName);
String url = getResAccessUrl(storePath);
fileInfo.setUrl(url);
fileInfo.setPath(path.substring(0, path.lastIndexOf("/")));
}
@Override
public InputStream download(String fileUri) {
return download(properties.getBucket(), fileUri);
}
@Override
public InputStream download(String bucket, String fileUri) {
return storageClient.downloadFile(bucket, fileUri, ins -> ins);
}
@Override
public Boolean delete(String fileUri) {
return delete(properties.getBucket(), fileUri);
}
@Override
public Boolean delete(String bucket, String fileUri) {
storageClient.deleteFile(bucket, fileUri);
return Boolean.TRUE;
}
/**
* 封装图片完整URL地址
*/
private String getResAccessUrl(StorePath storePath) {
return fdfsWebServer.getWebServerUrl() + storePath.getFullPath();
}
}

View File

@@ -0,0 +1,96 @@
package cn.fateverse.common.file.service.impl;
import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.common.file.entity.FileInfo;
import cn.fateverse.common.file.service.FileStoreService;
import cn.fateverse.common.file.service.MinioFileService;
import cn.fateverse.common.file.utils.FileStoreServiceUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
/**
* minio 对象存储实现类
*
* @author Clay
* @date 2023-02-16
*/
@Slf4j
public class MinioFileStoreService implements FileStoreService {
private final MinioFileService minioFileService;
private final String bucket;
public MinioFileStoreService(MinioFileService minioFileService) {
this.minioFileService = minioFileService;
this.bucket = minioFileService.getBucket();
}
@Override
public Boolean createBucket(String bucketName) {
return minioFileService.createBucket(bucketName);
}
@Override
public Boolean deleteBucket(String bucketName) {
return minioFileService.deleteBucket(bucketName);
}
@Override
public FileInfo upload(MultipartFile file) {
return upload(bucket, file);
}
@Override
public FileInfo upload(String bucket, MultipartFile file) {
// 上传文件流
FileInfo fileInfo = FileStoreServiceUtil.getOssFile(file);
boolean upload = minioFileService.upload(bucket, file, fileInfo);
if (!upload) {
throw new CustomException("文件上传失败!");
}
String url = minioFileService.getEndpoint() + "/" + bucket + "/" + fileInfo.getUri();
fileInfo.setUrl(url);
return fileInfo;
}
@Override
public FileInfo upload(InputStream file, String fileName) {
// 上传文件流
FileInfo fileInfo = FileStoreServiceUtil.getFileInfo(file, fileName);
boolean upload = minioFileService.upload(bucket, file, fileInfo);
if (!upload) {
throw new CustomException("文件上传失败!");
}
String url = minioFileService.getEndpoint() + "/" + bucket + "/" + fileInfo.getUri();
fileInfo.setUrl(url);
return fileInfo;
}
@Override
public FileInfo upload(String bucket, InputStream file, String fileName) {
return null;
}
@Override
public InputStream download(String fileUri) {
return minioFileService.getStream(bucket, fileUri);
}
@Override
public InputStream download(String bucket, String fileUri) {
return minioFileService.getStream(bucket, fileUri);
}
@Override
public Boolean delete(String fileUri) {
return minioFileService.delete(bucket, fileUri);
}
@Override
public Boolean delete(String bucket, String fileUri) {
return minioFileService.delete(bucket, fileUri);
}
}

View File

@@ -0,0 +1,147 @@
package cn.fateverse.common.file.utils;
import cn.fateverse.common.core.utils.HttpServletUtils;
import cn.fateverse.common.file.entity.FileInfo;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Arrays;
import java.util.Date;
import java.util.UUID;
import java.util.function.Consumer;
/**
* 对象存储服务工具
*
* @author Clay
* @date 2023-02-16
*/
public class FileStoreServiceUtil {
// 允许上传文件(图片)的格式
public static final String[] IMAGE_TYPE = new String[]{"bmp", "jpg",
"jpeg", "gif", "png"};
public static FileInfo getOssFile(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
return getFileInfo(file.getSize(), originalFilename);
}
@NotNull
public static FileInfo getFileInfo(InputStream file, String originalFilename) {
try {
return getFileInfo(file.available(),originalFilename);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@NotNull
public static FileInfo getFileInfo(long fileSize, String originalFilename) {
// 获取文件类型
assert originalFilename != null;
String fileType = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
UUID uuid = UUID.randomUUID();
String fileName = uuid + "." + fileType;
String format = DateFormatUtils.format(new Date(), "yyyy/MM/dd");
//根据时间拼接url
String uri = format + "/" + fileName;
FileInfo fileInfo = FileInfo.builder()
.fileName(fileName)
.originalFilename(originalFilename)
.uri(uri)
.path(format)
.fileType(fileType)
.size(fileSize)
.build();
fileInfo.setIsImage(Arrays.asList(IMAGE_TYPE).contains(fileType));
if (fileInfo.getIsImage()){
fileInfo.setThumbnailUri(format+"/"+fileInfo.getThumbnailFileName());
fileInfo.setThumbnailFileName(uuid + ".min." + fileType);
}
return fileInfo;
}
public static void writeTo(InputStream inputStream, OutputStream os) throws IOException {
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes)) != -1) {
os.write(bytes, 0, len);
}
os.flush();
}
public static InputStream getInputStream(MultipartFile file) {
// 获取oss的地域节点
try {
return file.getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 生成缩略图并缩放到指定大小,默认输出图片格式通过 thumbnailSuffix 获取
*
* @return
*/
public static byte[] thumbnail(int width, int height, InputStream inputStream, FileInfo fileInfo) {
return thumbnail(th -> th.size(width, height), inputStream, fileInfo);
}
/**
* 通过指定 InputStream 生成缩略图并进行图片处理,
* 可以进行裁剪、旋转、缩放、水印等操作,默认输出图片格式通过 thumbnailSuffix 获取,
* 操作完成后会自动关闭 InputStream
*
* @return
*/
public static byte[] thumbnail(Consumer<Thumbnails.Builder<? extends InputStream>> consumer, InputStream in, FileInfo fileInfo) {
try {
Thumbnails.Builder<? extends InputStream> builder = Thumbnails.of(in);
//builder.outputFormat(file.getFileType());
consumer.accept(builder);
ByteArrayOutputStream out = new ByteArrayOutputStream();
builder.toOutputStream(out);
return out.toByteArray();
} catch (IOException e) {
throw new RuntimeException("生成缩略图失败!", e);
}
}
public static byte[] thumbnail(InputStream inputStream, FileInfo fileInfo) {
return thumbnail(200, 200, inputStream, fileInfo);
}
public static void downloadFile(InputStream inputStream, String fileName){
HttpServletResponse response = HttpServletUtils.getResponse();
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
byte[] bytes = outputStream.toByteArray();
IOUtils.write(bytes,response.getOutputStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition" ,
"attachment;fileName=" + fileName);
}
}

View File

@@ -0,0 +1,105 @@
{
"groups": [
{
"name": "file.store.aliyun",
"type": "cn.fateverse.oss.config.AliyunProperties",
"sourceType": "cn.fateverse.oss.config.AliyunProperties"
},
{
"name": "file.store.minio",
"type": "cn.fateverse.oss.config.MinioProperties",
"sourceType": "cn.fateverse.oss.config.MinioProperties"
},
{
"name": "file.store.ftp",
"type": "cn.fateverse.oss.config.FTPProperties",
"sourceType": "cn.fateverse.oss.config.FTPProperties"
}
],
"properties": [
{
"name": "file.store.aliyun.endpoint",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.AliyunProperties"
},
{
"name": "file.store.aliyun.access-key-id",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.AliyunProperties"
},
{
"name": "file.store.aliyun.secret-access-key",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.AliyunProperties"
},
{
"name": "file.store.aliyun.bucket",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.AliyunProperties"
},
{
"name": "file.store.aliyun.file-host",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.AliyunProperties"
},{
"name": "file.store.minio.access-key",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.MinioProperties"
},
{
"name": "file.store.minio.secret-key",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.MinioProperties"
},
{
"name": "file.store.minio.bucket",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.MinioProperties"
},
{
"name": "file.store.minio.endpoint",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.MinioProperties"
},
{
"name": "file.store.ftp.address",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.FTPProperties"
},
{
"name": "file.store.ftp.port",
"type": "java.lang.Integer",
"sourceType": "cn.fateverse.oss.config.FTPProperties"
},
{
"name": "file.store.ftp.username",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.FTPProperties"
},
{
"name": "file.store.ftp.password",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.FTPProperties"
},
{
"name": "file.store.ftp.encoding",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.FTPProperties"
},
{
"name": "file.store.ftp.asset",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.FTPProperties"
},
{
"name": "file.store.ftp.pubfiles",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.FTPProperties"
},
{
"name": "file.store.ftp.prifiles",
"type": "java.lang.String",
"sourceType": "cn.fateverse.oss.config.FTPProperties"
}
]
}

View File

@@ -0,0 +1,5 @@
cn.fateverse.common.file.config.AliyunAutoConfiguration
cn.fateverse.common.file.config.MinioAutoConfiguration
cn.fateverse.common.file.config.FTPAutoConfiguration
cn.fateverse.common.file.config.FastDFSAutoConfiguration
cn.fateverse.common.file.service.FileStoreCombinationService