当我们配置了阿里云,腾讯云,minio等多个云存储厂商的业务代码时,如果我们要修改具体使用哪一种厂商的云存储,那么我们的controller层和service层就会需要改变业务代码;此时我们可以使用适配器模式来进行松耦合——>**1.**我们首先会定义一个关于文件存储的接口(非常丰富),然后定义minio,阿里云等厂商的文件存储实现类,去实现文件存储的具体细节——>**2.**那么如何确定具体使用哪一个文件存储?——>**3.**我们利用nacos动态路由,得到storage.type——>**4.**然后再在我们的StorageConfig配置类中进行判断,如果是minio的,就返回minio的业务实现类,将其注入容器中,这样就实现了我们的动态路由,我们只需修改nacos上的配置文件进行发布即可
package com.wyh.oss.adapter;
import com.wyh.oss.entity.FileInfo;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.List;
/**
* 文件存储适配器
*
* @create: 2023/12/1 0:32
*/
public interface StorageAdapter {
/**
* 创建存储桶
*
* @param bucket 存储桶名称
*/
void createBucket(String bucket);
/**
* 上传文件
*
* @param multipartFile 文件流
* @param bucket 存储桶名称
* @param objectName 对象名称
*/
void uploadFile(MultipartFile multipartFile, String bucket, String objectName);
/**
* 获取所有存储桶
*
* @return 存储桶名称集合
*/
List<String> getAllBuckets();
/**
* 获取存储桶下所有文件
*
* @param bucket 存储桶名称
* @return 文件信息集合
*/
List<FileInfo> getAllFiles(String bucket);
/**
* 下载文件
*
* @param bucket 存储桶名称
* @param objectName 对象名称
* @return 文件流
*/
InputStream download(String bucket, String objectName);
/**
* 删除存储桶
*
* @param bucket 存储桶名称
*/
void deleteBucket(String bucket);
/**
* 删除文件
*
* @param bucket 存储桶名称
* @param objectName 对象名称
*/
void deleteObject(String bucket, String objectName);
String getUrl(String bucketName, String objectName);
}
minio的业务实现类:
package com.wyh.oss.adapter;
import com.wyh.oss.entity.FileInfo;
import com.wyh.oss.util.MinioUtil;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.InputStream;
import java.util.List;
/**
* minioIO存储适配器
*
* @create: 2023/12/1 0:38
*/
public class MinioStorageAdapter implements StorageAdapter {
@Resource
private MinioUtil minioUtil;
/**
* minioUrl
*/
@Value("${minio.url}")
private String url;
// 创建存储桶
@Override
@SneakyThrows
public void createBucket(String bucket) {
minioUtil.createBucket(bucket);
}
// 文件上传
@Override
@SneakyThrows
public void uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
// 使用 minioUtil 对象创建存储桶
minioUtil.createBucket(bucket);
// 如果 objectName 不为空,则将文件名设置为 objectName + "/" + multipartFile.getName()
if (objectName != null) {
// 使用 minioUtil 对象将文件上传到 MinIO 存储服务中,并将其保存到指定的 bucket 以及 objectName + "/" + multipartFile.getName()
minioUtil.uploadFile(multipartFile.getInputStream(), bucket, objectName + "/" + multipartFile.getName());
} else {
// 使用 minioUtil 对象将文件上传到 MinIO 存储服务中,并将其保存到指定的 bucket 以及 multipartFile.getName()
minioUtil.uploadFile(multipartFile.getInputStream(), bucket, multipartFile.getName());
}
}
// 获取所有存储桶
@Override
@SneakyThrows
public List<String> getAllBuckets() {
return minioUtil.getAllBucket();
}
// 获取所有文件
@Override
@SneakyThrows
public List<FileInfo> getAllFiles(String bucket) {
return minioUtil.getAllFile(bucket);
}
// 文件下载
@Override
@SneakyThrows
public InputStream download(String bucket, String objectName) {
return minioUtil.downLoad(bucket, objectName);
}
// 桶删除
@Override
@SneakyThrows
public void deleteBucket(String bucket) {
minioUtil.deleteBucket(bucket);
}
// 文件删除
@Override
@SneakyThrows
public void deleteObject(String bucket, String objectName) {
minioUtil.deleteObject(bucket, objectName);
}
@Override
@SneakyThrows
public String getUrl(String bucket, String objectName) {
return url + "/" + bucket + "/" + objectName;
}
}
Util中封装了所有存储的具体细节:
package com.wyh.oss.util;
import com.wyh.oss.entity.FileInfo;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
/**
* minio文件操作工具
*
* @author: ChickenWing
* @date: 2023/10/11
*/
@Component
public class MinioUtil {
@Resource
private MinioClient minioClient;
/**
* 创建bucket桶
*/
public void createBucket(String bucket) throws Exception {
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
if (!exists) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
}
}
/**
* 上传文件
*/
public void uploadFile(InputStream inputStream, String bucket, String objectName) throws Exception {
minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(objectName)
.stream(inputStream, -1, 5242889L).build());
}
/**
* 列出所有桶
*/
public List<String> getAllBucket() throws Exception {
List<Bucket> buckets = minioClient.listBuckets();
return buckets.stream().map(Bucket::name).collect(Collectors.toList());
}
/**
* 列出当前桶及文件
*/
public List<FileInfo> getAllFile(String bucket) throws Exception {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucket).build());
List<FileInfo> fileInfoList = new LinkedList<>();
for (Result<Item> result : results) {
FileInfo fileInfo = new FileInfo();
Item item = result.get();
fileInfo.setFileName(item.objectName());
fileInfo.setDirectoryFlag(item.isDir());
fileInfo.setEtag(item.etag());
fileInfoList.add(fileInfo);
}
return fileInfoList;
}
/**
* 下载文件
*/
public InputStream downLoad(String bucket, String objectName) throws Exception {
return minioClient.getObject(
GetObjectArgs.builder().bucket(bucket).object(objectName).build()
);
}
/**
* 删除桶
*/
public void deleteBucket(String bucket) throws Exception {
minioClient.removeBucket(
RemoveBucketArgs.builder().bucket(bucket).build()
);
}
/**
* 删除文件
*/
public void deleteObject(String bucket, String objectName) throws Exception {
minioClient.removeObject(
RemoveObjectArgs.builder().bucket(bucket).object(objectName).build()
);
}
/**
* 获取文件url
*/
public String getPreviewFileUrl(String bucketName, String objectName) throws Exception{
GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName).object(objectName).build();
return minioClient.getPresignedObjectUrl(args);
}
}
config的配置:
package com.wyh.oss.config;
import com.wyh.oss.adapter.AliStorageAdapter;
import com.wyh.oss.adapter.MinioStorageAdapter;
import com.wyh.oss.adapter.StorageAdapter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 文件存储config
*
* @create: 2023/12/1 10:35
*/
@Configuration
@RefreshScope
public class StorageConfig {
@Value("${storage.service.type}")
private String storageType;
/*@Resource
private StorageAdapter aliStorageAdapterImpl;
@Resource
private StorageAdapter minioStorageServiceImpl;*/
@Bean
@RefreshScope
public StorageAdapter storageService() {
if ("minio".equals(storageType)){
return new MinioStorageAdapter();
} else if("aliyun".equals(storageType)) {
return new AliStorageAdapter();
} else {
throw new IllegalArgumentException("未配置存储服务类型");
}
}
}