流程
当我们配置了阿里云,腾讯云,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 {@Resourceprivate MinioUtil minioUtil;/*** minioUrl*/@Value("${minio.url}")private String url;// 创建存储桶@Override@SneakyThrowspublic void createBucket(String bucket) {minioUtil.createBucket(bucket);}// 文件上传@Override@SneakyThrowspublic 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@SneakyThrowspublic List<String> getAllBuckets() {return minioUtil.getAllBucket();}// 获取所有文件@Override@SneakyThrowspublic List<FileInfo> getAllFiles(String bucket) {return minioUtil.getAllFile(bucket);}// 文件下载@Override@SneakyThrowspublic InputStream download(String bucket, String objectName) {return minioUtil.downLoad(bucket, objectName);}// 桶删除@Override@SneakyThrowspublic void deleteBucket(String bucket) {minioUtil.deleteBucket(bucket);}// 文件删除@Override@SneakyThrowspublic void deleteObject(String bucket, String objectName) {minioUtil.deleteObject(bucket, objectName);}@Override@SneakyThrowspublic 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 {@Resourceprivate 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;/*@Resourceprivate StorageAdapter aliStorageAdapterImpl;@Resourceprivate StorageAdapter minioStorageServiceImpl;*/@Bean@RefreshScopepublic StorageAdapter storageService() {if ("minio".equals(storageType)){return new MinioStorageAdapter();} else if("aliyun".equals(storageType)) {return new AliStorageAdapter();} else {throw new IllegalArgumentException("未配置存储服务类型");}}}