Nacos 配置中心 + OSS / Minio 文件上传实战
Nacos 配置中心 OSS / Minio 文件上传实战环境组件版本Spring Boot3.5.16Spring Cloud2025.0.0Spring Cloud Alibaba2025.0.0.0Nacos Server3.0.3Java17Nacos 控制台怎么创建配置文件登录 Nacos 控制台http://127.0.0.1:8848/nacos左侧菜单配置管理 → 配置列表点右上角号。Data ID idle-store-oss-dev.yaml Group DEFAULT_GROUP 配置格式 YAML 命名空间 idlestore 配置内容 见下面Data ID 的命名规则是{spring.application.name}-{profile}.{file-extension}。项目里application-dev.yml写的optional:nacos:idle-store-oss-dev.yaml所以 Data ID 就是idle-store-oss-dev.yaml。注意spring.config.import方式不会自动读取spring.application.name来拼 Data ID你得在 import URL 里写完整。这和 bootstrap 方式不一样老方式只需要配prefix和file-extension框架自动拼。OSS Minio 完整 Nacos 配置项目里用一个storage.type来控制走阿里云 OSS 还是 MinioFileStrategyFactory根据这个值返回对应的策略 Bean。两套 SDK 的 client 也都是从配置读参数创建的。Nacos 里idle-store-oss-dev.yaml的完整内容storage:type:aliyunminio:endpoint:http://127.0.0.1:9000accessKey:adminsecretKey:12345678aliyun-oss:accessKey:xxxsecretKey:xxxendpoint:oss-cn-beijing.aliyuncs.com每个字段的含义字段含义示例值storage.type切换存储类型aliyun/miniostorage.minio.endpointMinio 服务地址http://127.0.0.1:9000storage.minio.accessKeyMinio 用户名adminstorage.minio.secretKeyMinio 密码12345678storage.aliyun-oss.accessKey阿里云 RAM AccessKeyLTAI5...storage.aliyun-oss.secretKey阿里云 RAM SecretKeywxsck...storage.aliyun-oss.bucketOSS Bucket 名称typora-picture-0storage.aliyun-oss.endpointOSS 地域 Endpointoss-cn-beijing.aliyuncs.com本地application-dev.yml只要保留 Nacos 连接信息不要写这些业务配置spring:application:name:idle-store-ossconfig:import:-optional:nacos:idle-store-oss-dev.yaml?groupDEFAULT_GROUPrefreshEnabledtruecloud:nacos:config:group:DEFAULT_GROUPserver-addr:127.0.0.1:8848namespace:idlestorediscovery:enabled:truegroup:DEFAULT_GROUPnamespace:idlestoreserver-addr:127.0.0.1:8848代码怎么绑这些配置完整的工程结构idle-store-oss/idle-store-oss-biz/src/main/java/com/lh/idlestore/oss/biz/ ├── config/ │ ├── StorageProperties.java ← storage.type │ ├── MinioProperties.java ← storage.minio.* │ ├── AliyunOSSProperties.java ← storage.aliyun-oss.* │ ├── AliyunOSSConfig.java ← 创建 OSS Client │ └── MinioConfig.java ← 创建 Minio Client ├── strategy/ │ ├── FileStrategy.java ← 上传接口 │ └── impl/ │ ├── AliyunOSSFileStrategy.java │ └── MinioFileStrategy.java ├── factory/ │ └── FileStrategyFactory.java ← 根据 storage.type 选策略 ├── controller/ │ └── FileController.java ← 上传接口 └── service/ ├── FileService.java └── impl/ └── FileServiceImpl.javaProperties 类——三层配置对应三个类// storage.typeConfigurationProperties(storage)ComponentRefreshScopeDatapublicclassStorageProperties{privateStringtype;}// storage.minioConfigurationProperties(storage.minio)ComponentRefreshScopeDatapublicclassMinioProperties{privateStringendpoint;privateStringaccessKey;privateStringsecretKey;}// storage.aliyun-ossConfigurationProperties(storage.aliyun-oss)ComponentRefreshScopeDatapublicclassAliyunOSSProperties{privateStringendpoint;privateStringaccessKey;privateStringsecretKey;}prefix用.分割Spring Boot 自动把 yaml 的嵌套结构映射到对应的 Properties 类。Client 创建——从 Properties 读参数// 阿里云 OSS ClientConfigurationpublicclassAliyunOSSConfig{ResourceprivateAliyunOSSPropertiesaliyunOSSProperties;BeanpublicOSSaliyunOSSClient(){DefaultCredentialProvidercredentialsProviderCredentialsProviderFactory.newDefaultCredentialProvider(aliyunOSSProperties.getAccessKey(),aliyunOSSProperties.getSecretKey());returnnewOSSClientBuilder().build(aliyunOSSProperties.getEndpoint(),credentialsProvider);}}// Minio ClientConfigurationpublicclassMinioConfig{ResourceprivateMinioPropertiesminioProperties;BeanpublicMinioClientminioClient(){returnMinioClient.builder().endpoint(minioProperties.getEndpoint()).credentials(minioProperties.getAccessKey(),minioProperties.getSecretKey()).build();}}策略工厂——用 storage.type 决定走哪个ConfigurationSlf4jpublicclassFileStrategyFactory{AutowiredprivateStoragePropertiesstorageProperties;BeanpublicFileStrategygetFileStrategy(){log.info(配置变为了: {},storageProperties.getType());if(StringUtils.equals(storageProperties.getType(),minio)){returnnewMinioFileStrategy();}elseif(StringUtils.equals(storageProperties.getType(),aliyun)){returnnewAliyunOSSFileStrategy();}thrownewIllegalArgumentException(不可用的存储类型);}}注意FileStrategyFactory上没有RefreshScopeStorageProperties上有。因为StorageProperties加了RefreshScope它的代理每次getType()都会从 Environment 拿最新值所以工厂不需要自己加RefreshScope。文件上传——策略模式调用方不用管底层// 接口publicinterfaceFileStrategy{StringuploadFile(MultipartFilefile,StringbucketName);}// 阿里云 OSS 实现Slf4jComponent(aliyun)publicclassAliyunOSSFileStrategyimplementsFileStrategy{ResourceprivateAliyunOSSPropertiesaliyunOSSProperties;ResourceprivateOSSossClient;OverrideSneakyThrowspublicStringuploadFile(MultipartFilefile,StringbucketName){StringoriginalFileNamefile.getOriginalFilename();StringkeyUUID.randomUUID().toString().replace(-,);StringsuffixoriginalFileName.substring(originalFileName.lastIndexOf(.));StringobjectNameString.format(%s%s,key,suffix);ossClient.putObject(bucketName,objectName,newByteArrayInputStream(file.getInputStream().readAllBytes()));// OSS 访问 URL 格式https://{bucket}.{endpoint}/{objectName}returnString.format(https://%s.%s/%s,bucketName,aliyunOSSProperties.getEndpoint(),objectName);}}// Minio 实现Slf4jComponent(minio)publicclassMinioFileStrategyimplementsFileStrategy{ResourceprivateMinioPropertiesminioProperties;ResourceprivateMinioClientminioClient;OverrideSneakyThrowspublicStringuploadFile(MultipartFilefile,StringbucketName){StringoriginalFileNamefile.getOriginalFilename();StringcontentTypefile.getContentType();StringkeyUUID.randomUUID().toString().replace(-,);StringsuffixoriginalFileName.substring(originalFileName.lastIndexOf(.));StringobjectNameString.format(%s%s,key,suffix);minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(file.getInputStream(),file.getSize(),-1L).contentType(contentType).build());// Minio 访问 URL 格式http://{endpoint}/{bucket}/{objectName}returnString.format(%s/%s/%s,minioProperties.getEndpoint(),bucketName,objectName);}}阿里云 OSS 和 Minio 上传的区别阿里云 OSSMinioURL 格式https://{bucket}.{endpoint}/{objectName}http://{endpoint}/{bucket}/{objectName}putObject 方式ossClient.putObject(bucket, key, inputStream)minioClient.putObject(PutObjectArgs.builder()...)ContentType不需要手动设PutObjectArgs.builder().contentType(...)访问协议HTTPSHTTP本地 Minio调用入口RestControllerRequestMapping(/file)publicclassFileController{ResourceprivateFileServicefileService;PostMapping(value/upload,consumesMediaType.MULTIPART_FORM_DATA_VALUE)publicResponse?uploadFile(RequestPart(valuefile)MultipartFilefile){returnfileService.uploadFile(file);}}ServicepublicclassFileServiceImplimplementsFileService{ResourceprivateFileStrategyFactoryfileStrategyFactory;privatestaticfinalStringBUCKET_NAMEidlestore;OverridepublicResponse?uploadFile(MultipartFilefile){StringurlfileStrategyFactory.getFileStrategy().uploadFile(file,BUCKET_NAME);returnResponse.success(url);}}切换存储类型的完整链路在 Nacos 里把storage.type从aliyun改成minio不用重启推送变更RefreshScope 检测到变化getType() 返回新值返回 MinioFileStrategyNacos 控制台storage.type: aliyun → minioEnvironment 更新StoragePropertiestype 字段变为 minioFileStrategyFactorygetFileStrategy()FileServiceImpl调用 uploadFile()MinioClient.putObject()pom.xml 和 yml 模板依赖dependencies!-- Nacos 配置中心 --dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-config/artifactId/dependency!-- Nacos 服务发现 --dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependency!-- ❌ 不要引入 spring-cloud-starter-bootstrap --!-- 阿里云 OSS SDK --dependencygroupIdcom.aliyun.oss/groupIdartifactIdaliyun-sdk-oss/artifactId/dependency!-- Minio SDK --dependencygroupIdio.minio/groupIdartifactIdminio/artifactId/dependency!-- Spring Web --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency/dependenciesapplication-dev.ymlspring:application:name:idle-store-ossconfig:import:-optional:nacos:idle-store-oss-dev.yaml?groupDEFAULT_GROUPrefreshEnabledtruecloud:nacos:config:group:DEFAULT_GROUPserver-addr:127.0.0.1:8848namespace:idlestorediscovery:enabled:truegroup:DEFAULT_GROUPnamespace:idlestoreserver-addr:127.0.0.1:8848# 业务配置全部放 Nacos这里不留Nacos 里的 idle-store-oss-dev.yamlstorage:type:aliyunminio:endpoint:http://127.0.0.1:9000accessKey:adminsecretKey:12345678aliyun-oss:accessKey:your-aliyun-aksecretKey:your-aliyun-skendpoint:oss-cn-beijing.aliyuncs.com