引入依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.12</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.1</version>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.12.1</version>
</dependency>
代码实现
package com.meteor.blog.common.file.config;
import com.meteor.blog.common.file.constant.FileStoreTypeEnum;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "meteor.file")
public class FileStoreProperties {
private FileStoreTypeEnum type = FileStoreTypeEnum.LOCAL;
private String baseDir;
private String endpoint;
private String accessKey;
private String secretKey;
private String bucket;
private String nginxHost;
}
package com.meteor.blog.common.file;
import com.meteor.blog.common.file.constant.FileStoreTypeEnum;
import com.meteor.blog.common.file.service.FileTemplate;
import com.meteor.blog.common.file.service.FileTemplateImpl;
import com.meteor.blog.common.file.strategy.*;
import com.meteor.blog.common.file.config.FileStoreProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(FileStoreProperties.class)
public class FileAutoConfiguration {
@Bean
public FileStrategy fileStrategy(FileStoreProperties fileStoreProperties) {
FileStoreTypeEnum type = fileStoreProperties.getType();
switch (type) {
case MINIO -> {
return new MinioStrategyImpl(fileStoreProperties);
}
case ALIYUN -> {
return new AliyunStrategyImpl(fileStoreProperties);
}
case QINIU -> {
return new QiniuStrategyImpl(fileStoreProperties);
}
default -> {
return new LocalStrategyImpl(fileStoreProperties);
}
}
}
@Bean
public FileTemplate fileService(FileStrategy fileStrategy) {
return new FileTemplateImpl(fileStrategy);
}
}
package com.meteor.blog.common.file.config;
import com.meteor.blog.common.file.constant.FileStoreTypeEnum;
import com.meteor.blog.common.file.strategy.FileStrategy;
import org.springframework.util.ObjectUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public final class StrategyFactory {
private static final Map<FileStoreTypeEnum, FileStrategy> STRATEGY_MAP = new ConcurrentHashMap<>();
public static FileStrategy getStrategy(FileStoreTypeEnum type) {
return STRATEGY_MAP.get(type);
}
public static void register(FileStoreTypeEnum type, FileStrategy strategy) {
if (ObjectUtils.isEmpty(type)) {
return;
}
STRATEGY_MAP.put(type, strategy);
}
}
package com.meteor.blog.common.file.strategy;
import org.springframework.beans.factory.InitializingBean;
import java.io.InputStream;
import java.util.List;
public interface FileStrategy extends InitializingBean {
boolean directoryExists(String directory) throws Exception;
List<String> listDirectories() throws Exception;
void createDirectory(String directory) throws Exception;
void removeDirectory(String directory) throws Exception;
List<String> listFiles() throws Exception;
List<String> listFiles(String directory) throws Exception;
void uploadFile(String fileName, InputStream inputStream) throws Exception;
void uploadFile(String directory, String fileName, InputStream inputStream) throws Exception;
void removeFile(String directory, String fileName) throws Exception;
void removeFiles(String directory, List<String> fileNames) throws Exception;
InputStream downloadFile(String directory, String fileName) throws Exception;
}
package com.meteor.blog.common.file.strategy;
import cn.hutool.core.util.StrUtil;
import com.meteor.blog.common.base.util.SymbolUtil;
import com.meteor.blog.common.file.constant.FileStoreTypeEnum;
import com.meteor.blog.common.file.config.FileStoreProperties;
import com.meteor.blog.common.file.config.StrategyFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Slf4j
@RequiredArgsConstructor
public class LocalStrategyImpl implements FileStrategy {
private String baseDir;
public LocalStrategyImpl(FileStoreProperties fileStoreProperties) {
this.baseDir = fileStoreProperties.getBaseDir();
}
@Override
public boolean directoryExists(String directory) throws Exception {
return new File(this.baseDir + File.separator + directory).exists();
}
@Override
public List<String> listDirectories() throws Exception {
File file = new File(this.baseDir);
List<String> list = new ArrayList<>();
for (File subFile : Objects.requireNonNull(file.listFiles())) {
if (subFile.isDirectory()) {
list.add(subFile.getName());
}
}
return list;
}
@Override
public void createDirectory(String directory) throws Exception {
File file = new File(this.baseDir + File.separator + directory);
if (!file.exists()) {
file.mkdirs();
}
}
@Override
public void removeDirectory(String directory) throws Exception {
File file = new File(this.baseDir + File.separator + directory);
if (file.exists()) {
file.delete();
}
}
@Override
public List<String> listFiles() throws Exception {
return this.listFiles(null);
}
@Override
public List<String> listFiles(String directory) throws Exception {
File file = new File(this.baseDir + (StrUtil.isBlank(directory) ? "" : File.separator + directory));
List<String> list = new ArrayList<>();
for (File subFile : Objects.requireNonNull(file.listFiles())) {
if (subFile.isFile()) {
list.add(subFile.getName());
}
}
return list;
}
@Override
public void uploadFile(String fileName, InputStream inputStream) throws Exception {
this.uploadFile(this.baseDir, fileName, inputStream);
}
@Override
public void uploadFile(String directory, String fileName, InputStream inputStream) throws Exception {
try (FileOutputStream outputStream = new FileOutputStream(directory + SymbolUtil.SLASH + fileName)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
throw new RuntimeException("Failed to upload file to local storage", e);
}
}
@Override
public void removeFile(String directory, String fileName) throws Exception {
File file = new File(directory + File.separator + fileName);
if (file.exists()) {
file.delete();
}
}
@Override
public void removeFiles(String directory, List<String> fileNames) throws Exception {
for (String fileName : fileNames) {
this.removeFile(directory, fileName);
}
}
@Override
public InputStream downloadFile(String directory, String fileName) throws Exception {
try {
return new FileInputStream(this.baseDir + (StrUtil.isBlank(directory) ? "" : File.separator + directory) + File.separator + fileName);
} catch (IOException e) {
throw new RuntimeException("Failed to download file from local storage", e);
}
}
@Override
public void afterPropertiesSet() throws Exception {
StrategyFactory.register(FileStoreTypeEnum.LOCAL, this);
}
}
package com.meteor.blog.common.file.strategy;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.meteor.blog.common.base.util.SymbolUtil;
import com.meteor.blog.common.file.constant.FileStoreTypeEnum;
import com.meteor.blog.common.file.config.FileStoreProperties;
import com.meteor.blog.common.file.config.StrategyFactory;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@RequiredArgsConstructor
public class MinioStrategyImpl implements FileStrategy {
private MinioClient minioClient;
private String bucket;
public MinioStrategyImpl(FileStoreProperties fileStoreProperties) {
this.minioClient = MinioClient.builder()
.credentials(fileStoreProperties.getAccessKey(), fileStoreProperties.getSecretKey())
.endpoint(fileStoreProperties.getEndpoint())
.build();
this.bucket = fileStoreProperties.getBucket();
}
@Override
public boolean directoryExists(String directory) throws Exception {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(directory).build());
}
@Override
public List<String> listDirectories() throws Exception {
return minioClient.listBuckets().stream().map(Bucket::name).collect(Collectors.toList());
}
@Override
public void createDirectory(String directory) throws Exception {
if (!this.directoryExists(directory)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(directory).build());
}
}
@Override
public void removeDirectory(String directory) throws Exception {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(directory).build());
}
@Override
public List<String> listFiles() throws Exception {
return this.listFiles(this.bucket);
}
@Override
public List<String> listFiles(String directory) throws Exception {
List<String> list = new ArrayList<>();
Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(directory).build());
for (Result<Item> result : results) {
list.add(result.get().objectName());
}
return list;
}
@Override
public void uploadFile(String fileName, InputStream inputStream) throws Exception {
this.uploadFile(this.bucket, fileName, inputStream);
}
@Override
public void uploadFile(String directory, String fileName, InputStream inputStream) throws Exception {
this.createDirectory(directory);
minioClient.putObject(PutObjectArgs.builder()
.bucket(directory)
.object(fileName)
.stream(inputStream, inputStream.available(), -1)
.contentType("")
.build());
}
@Override
public void removeFile(String directory, String fileName) throws Exception {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(directory).object(fileName).build());
}
@Override
public void removeFiles(String directory, List<String> fileNames) throws Exception {
List<DeleteObject> deleteObjectList = new ArrayList<>();
for (String fileName : fileNames) {
DeleteObject deleteObject = new DeleteObject(fileName);
deleteObjectList.add(deleteObject);
}
Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(directory).objects(deleteObjectList).build());
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
log.error("Error in deleting object {}, reason: {}", error.objectName(), error.message());
}
}
@Override
public InputStream downloadFile(String directory, String fileName) throws Exception {
GetObjectResponse object = minioClient.getObject(GetObjectArgs.builder().bucket(directory).object(fileName).build());
String object1 = object.object();
return null;
}
@Override
public void afterPropertiesSet() throws Exception {
StrategyFactory.register(FileStoreTypeEnum.MINIO, this);
}
}
package com.meteor.blog.common.file.strategy;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import com.meteor.blog.common.file.constant.FileStoreTypeEnum;
import com.meteor.blog.common.file.config.FileStoreProperties;
import com.meteor.blog.common.file.config.StrategyFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@RequiredArgsConstructor
public class AliyunStrategyImpl implements FileStrategy {
private OSS ossClient;
private String bucket;
public AliyunStrategyImpl(FileStoreProperties fileStoreProperties) {
this.ossClient = new OSSClientBuilder()
.build(fileStoreProperties.getEndpoint(),
fileStoreProperties.getAccessKey(),
fileStoreProperties.getSecretKey());
this.bucket = fileStoreProperties.getBucket();
}
@Override
public boolean directoryExists(String directory) throws Exception {
return ossClient.doesBucketExist(directory);
}
@Override
public List<String> listDirectories() throws Exception {
return ossClient.listBuckets().stream().map(Bucket::getName).collect(Collectors.toList());
}
@Override
public void createDirectory(String directory) throws Exception {
if (!this.directoryExists(directory)) {
ossClient.createBucket(directory);
}
}
@Override
public void removeDirectory(String directory) throws Exception {
ossClient.deleteBucket(directory);
}
@Override
public List<String> listFiles() throws Exception {
return this.listFiles(this.bucket);
}
@Override
public List<String> listFiles(String directory) throws Exception {
ObjectListing objectListing = ossClient.listObjects(directory);
return objectListing.getObjectSummaries().stream().map(e -> e.getKey()).collect(Collectors.toList());
}
@Override
public void uploadFile(String directory, InputStream inputStream) throws Exception {
this.uploadFile(directory, this.bucket, inputStream);
}
@Override
public void uploadFile(String directory, String fileName, InputStream inputStream) throws Exception {
this.createDirectory(directory);
ossClient.putObject(directory, fileName, inputStream);
}
@Override
public void removeFile(String directory, String fileName) throws Exception {
ossClient.deleteObject(directory, fileName);
}
@Override
public void removeFiles(String directory, List<String> fileNames) throws Exception {
ossClient.deleteObjects(new DeleteObjectsRequest(directory).withKeys(fileNames));
}
@Override
public InputStream downloadFile(String directory, String fileName) throws Exception {
OSSObject object = ossClient.getObject(new GetObjectRequest(directory, fileName));
return object.getObjectContent();
}
@Override
public void afterPropertiesSet() throws Exception {
StrategyFactory.register(FileStoreTypeEnum.ALIYUN, this);
}
}
package com.meteor.blog.common.file.strategy;
import com.meteor.blog.common.file.constant.FileStoreTypeEnum;
import com.meteor.blog.common.file.config.FileStoreProperties;
import com.meteor.blog.common.file.config.StrategyFactory;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.storage.model.FileListing;
import com.qiniu.util.Auth;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Slf4j
@RequiredArgsConstructor
public class QiniuStrategyImpl implements FileStrategy {
private Auth auth;
private String bucket;
private BucketManager bucketManager;
private UploadManager uploadManager;
public QiniuStrategyImpl(FileStoreProperties fileStoreProperties) {
Configuration cfg = new Configuration(Region.autoRegion());
this.auth = Auth.create(fileStoreProperties.getAccessKey(), fileStoreProperties.getSecretKey());
this.bucket = fileStoreProperties.getBucket();
this.bucketManager = new BucketManager(this.auth, cfg);
this.uploadManager = new UploadManager(cfg);
}
@Override
public boolean directoryExists(String directory) throws Exception {
return this.listDirectories().contains(directory);
}
@Override
public List<String> listDirectories() throws Exception {
return Arrays.asList(bucketManager.buckets());
}
@Override
public void createDirectory(String directory) throws Exception {
if (!this.directoryExists(directory)) {
bucketManager.createBucket(directory, null);
}
}
@Override
public void removeDirectory(String directory) throws Exception {
bucketManager.delete(directory, null);
}
@Override
public List<String> listFiles() throws Exception {
return this.listFiles(this.bucket);
}
@Override
public List<String> listFiles(String directory) throws Exception {
FileListing fileListing = bucketManager.listFiles(directory, null, null, -1, null);
List<String> list = new ArrayList<>();
for (FileInfo fileInfo : fileListing.items) {
list.add(fileInfo.key);
}
return list;
}
@Override
public void uploadFile(String fileName, InputStream inputStream) throws Exception {
this.uploadFile(this.bucket, fileName, inputStream);
}
@Override
public void uploadFile(String directory, String fileName, InputStream inputStream) throws Exception {
String token = auth.uploadToken(directory);
uploadManager.put(inputStream, fileName, token, null, null);
}
@Override
public void removeFile(String directory, String fileName) throws Exception {
bucketManager.delete(directory, fileName);
}
@Override
public void removeFiles(String directory, List<String> fileNames) throws Exception {
BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();
batchOperations.addDeleteOp(directory, fileNames.toArray(new String[0]));
bucketManager.batch(batchOperations);
}
@Override
public InputStream downloadFile(String directory, String fileName) throws Exception {
auth.privateDownloadUrl(fileName);
return null;
}
@Override
public void afterPropertiesSet() throws Exception {
StrategyFactory.register(FileStoreTypeEnum.QINIU, this);
}
}
package com.meteor.blog.common.file.constant;
import com.meteor.blog.common.base.constant.BaseEnum;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public enum FileStoreTypeEnum implements BaseEnum<String> {
ALIYUN("aliyun", "阿里云"),
QINIU("qiniu", "七牛云"),
MINIO("minio", "minio"),
LOCAL("local", "本地");
private final String code;
private final String message;
}
package com.meteor.blog.common.file.service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.List;
public interface FileTemplate {
boolean directoryExists(String directory) throws Exception;
List<String> listDirectories() throws Exception;
void createDirectory(String directory) throws Exception;
void removeDirectory(String directory) throws Exception;
List<String> listFiles() throws Exception;
String uploadFile(String fileName, InputStream inputStream) throws Exception;
String uploadFile(String directory, String fileName, InputStream inputStream) throws Exception;
void removeFile(String directory, String fileName) throws Exception;
void removeFiles(String directory, List<String> fileNames) throws Exception;
InputStream downloadFile(String directory, String fileName) throws Exception;
}
package com.meteor.blog.common.file.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.meteor.blog.common.file.strategy.FileStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.util.Assert;
import java.io.InputStream;
import java.util.List;
@RequiredArgsConstructor
public class FileTemplateImpl implements FileTemplate {
private final FileStrategy fileStrategy;
@Override
public boolean directoryExists(String directory) throws Exception {
Assert.isTrue(StrUtil.isNotBlank(directory), "The directory cannot be blank");
return fileStrategy.directoryExists(directory);
}
@Override
public List<String> listDirectories() throws Exception {
return fileStrategy.listDirectories();
}
@Override
public void createDirectory(String directory) throws Exception {
Assert.isTrue(StrUtil.isNotBlank(directory), "The directory cannot be blank");
fileStrategy.createDirectory(directory);
}
@Override
public void removeDirectory(String directory) throws Exception {
Assert.isTrue(StrUtil.isNotBlank(directory), "The directory cannot be blank");
fileStrategy.removeDirectory(directory);
}
@Override
public List<String> listFiles() throws Exception {
return fileStrategy.listFiles();
}
@Override
public String uploadFile(String fileName, InputStream inputStream) throws Exception {
Assert.isTrue(StrUtil.isNotBlank(fileName), "The fileName cannot be blank");
Assert.notNull(inputStream, "The inputStream cannot be null");
fileStrategy.uploadFile(fileName, inputStream);
return null;
}
@Override
public String uploadFile(String directory, String fileName, InputStream inputStream) throws Exception {
fileStrategy.uploadFile(directory, fileName, inputStream);
return "";
}
@Override
public void removeFile(String directory, String fileName) throws Exception {
Assert.isTrue(StrUtil.isNotBlank(directory), "The directory cannot be blank");
Assert.isTrue(StrUtil.isNotBlank(fileName), "The fileName cannot be blank");
fileStrategy.removeFile(directory, fileName);
}
@Override
public void removeFiles(String directory, List<String> fileNames) throws Exception {
Assert.isTrue(StrUtil.isNotBlank(directory), "The directory cannot be blank");
Assert.isTrue(CollUtil.isNotEmpty(fileNames), "The fileNames cannot be empty");
fileStrategy.removeFiles(directory, fileNames);
}
@Override
public InputStream downloadFile(String directory, String fileName) throws Exception {
Assert.isTrue(StrUtil.isNotBlank(directory), "The directory cannot be blank");
Assert.isTrue(StrUtil.isNotBlank(fileName), "The fileName cannot be blank");
return fileStrategy.downloadFile(directory, fileName);
}
}