init
This commit is contained in:
114
common/common-file/pom.xml
Normal file
114
common/common-file/pom.xml
Normal 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>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package cn.fateverse.common.file.config;
|
||||
|
||||
/**
|
||||
* 华为 OBS 配置信息
|
||||
* @author Clay
|
||||
* @date 2023-03-17
|
||||
*/
|
||||
public class HuaweiOBSProperties {
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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文件上传文件错误");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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登录失败");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user