当前位置: 首页 > news >正文

SpringBoot 整合 MinIO 实现文件存储——私有化 OSS 方案

项目里总少不了文件上传下载的功能——用户头像、合同附件、产品图片。用阿里云 OSS 方便但要钱,自己存服务器又麻烦。MinIO 是一个开源的对象存储服务,兼容 S3 协议,可以私有化部署,性能和功能完全不输商业 OSS。

一、MinIO 简介

MinIO vs 其他方案: 阿里云 OSS → 按量付费,省心但长期用成本高 FastDFS → 部署复杂,社区不活跃 MinIO → 开源免费,部署简单,性能强悍(号称读写 183GB/s) 自己存磁盘 → 简单但不支持分布式,备份困难

MinIO 的优势:

  • 兼容 AWS S3 接口,SDK 直接可用
  • 部署简单,一个 Docker 命令启动
  • 支持分布式部署(多台机器做集群)
  • 有 Web 管理界面
  • 开源且社区活跃

二、安装 MinIO

1. Docker 一键部署(推荐)

dockerrun-d\--nameminio\-p9000:9000\-p9001:9001\-eMINIO_ROOT_USER=admin\-eMINIO_ROOT_PASSWORD=admin123456\-vD:\minio\data:/data\quay.io/minio/minio server /data --console-address":9001"

启动后访问:

  • API 端口:http://localhost:9000
  • 管理后台http://localhost:9001(账号 admin / 密码 admin123456)

2. 在管理台创建 Bucket

登录管理后台 → 点击「Create Bucket」→ 输入名称(如my-bucket)→ 确认。

三、SpringBoot 集成 MinIO

1. 引入依赖

<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.7</version></dependency>

2. 配置

minio:endpoint:http://localhost:9000access-key:adminsecret-key:admin123456bucket:my-bucket

3. 配置类

@ConfigurationpublicclassMinIOConfig{@Value("${minio.endpoint}")privateStringendpoint;@Value("${minio.access-key}")privateStringaccessKey;@Value("${minio.secret-key}")privateStringsecretKey;@BeanpublicMinioClientminioClient(){returnMinioClient.builder().endpoint(endpoint).credentials(accessKey,secretKey).build();}}

四、文件上传下载

1. 文件上传服务

@ServicepublicclassFileService{@AutowiredprivateMinioClientminioClient;@Value("${minio.bucket}")privateStringbucket;/** * 上传文件 * @param file 上传的文件 * @param objectName 存储的文件名(如 avatar/2026/06/abc123.jpg) */publicStringupload(MultipartFilefile,StringobjectName)throwsException{// 检查 bucket 是否存在booleanfound=minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());if(!found){minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());}// 上传minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(objectName).stream(file.getInputStream(),file.getSize(),-1).contentType(file.getContentType()).build());// 返回可访问的 URLreturnendpoint+"/"+bucket+"/"+objectName;}/** * 上传文件(自动生成文件名) */publicStringupload(MultipartFilefile)throwsException{// 原始文件名StringoriginalFilename=file.getOriginalFilename();// 扩展名Stringext=originalFilename.substring(originalFilename.lastIndexOf("."));// 新文件名:日期 + UUIDStringobjectName=DateUtil.today()+"/"+IdUtil.simpleUUID()+ext;returnupload(file,objectName);}/** * 上传文件(指定目录前缀) */publicStringupload(MultipartFilefile,Stringprefix,LonguserId)throwsException{Stringext=originalFilename.substring(originalFilename.lastIndexOf("."));StringobjectName=prefix+"/"+userId+"/"+IdUtil.simpleUUID()+ext;returnupload(file,objectName);}}

2. Controller

@RestController@RequestMapping("/file")publicclassFileController{@AutowiredprivateFileServicefileService;@PostMapping("/upload")publicResultVO<String>upload(@RequestParam("file")MultipartFilefile){if(file.isEmpty()){returnResultVO.error(400,"请选择文件");}try{// 校验文件大小(10MB)if(file.getSize()>10*1024*1024){returnResultVO.error(400,"文件不能超过10MB");}// 校验文件类型(只允许图片和 PDF)StringcontentType=file.getContentType();if(contentType==null||!contentType.startsWith("image/")&&!contentType.equals("application/pdf")){returnResultVO.error(400,"不支持的文件格式");}Stringurl=fileService.upload(file);returnResultVO.success(url);}catch(Exceptione){returnResultVO.error(500,"上传失败: "+e.getMessage());}}@PostMapping("/upload/avatar")publicResultVO<String>uploadAvatar(@RequestParam("file")MultipartFilefile,@RequestParamLonguserId){try{Stringurl=fileService.upload(file,"avatar",userId);returnResultVO.success(url);}catch(Exceptione){returnResultVO.error(500,"上传失败");}}}

五、文件删除

publicvoiddelete(StringobjectName)throwsException{minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectName).build());}publicvoiddeleteByUrl(StringfileUrl){// 从 URL 中提取 objectName// http://localhost:9000/my-bucket/avatar/1/xxx.jpgStringprefix=endpoint+"/"+bucket+"/";StringobjectName=fileUrl.substring(prefix.length());delete(objectName);}

六、获取文件列表

publicList<String>listFiles(Stringprefix){List<String>files=newArrayList<>();Iterable<Result<Item>>results=minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).prefix(prefix)// 按前缀过滤.recursive(true)// 递归查询.build());for(Result<Item>result:results){files.add(result.get().objectName());}returnfiles;}

七、生成临时访问链接

有些文件不想公开访问,可以生成带有效期的临时链接:

publicStringgetPresignedUrl(StringobjectName,intexpiryMinutes)throwsException{returnminioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucket).object(objectName).method(Method.GET).expiry(expiryMinutes,TimeUnit.MINUTES).build());}

八、前端上传

<formid="uploadForm"enctype="multipart/form-data"><inputtype="file"name="file"id="fileInput"><buttontype="button"onclick="uploadFile()">上传</button></form><script>asyncfunctionuploadFile(){constfileInput=document.getElementById('fileInput');constformData=newFormData();formData.append('file',fileInput.files[0]);constresp=awaitfetch('/file/upload',{method:'POST',body:formData,});constresult=awaitresp.json();if(result.code===200){console.log('文件地址:',result.data);// 回显图片document.getElementById('preview').src=result.data;}}</script>

九、Nginx 代理 MinIO

生产环境中,MinIO 一般不直接暴露端口,而是通过 Nginx 代理:

server { listen 80; server_name file.example.com; location / { proxy_pass http://127.0.0.1:9000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

配置后访问http://file.example.com/my-bucket/xxx.jpg即可查看文件。

十、MinIO vs 阿里云 OSS 怎么选

场景推荐方案
个人/小项目,没有公网服务器阿里云 OSS(省心)
公司项目,服务器在本地机房MinIO(省成本)
高并发、大流量场景阿里云 OSS(CDN 加速)
数据隐私要求高(政务、金融)MinIO 私有化部署
学习/练手项目MinIO(Docker 几分钟搞定)

一句话:不差钱上阿里云 OSS,想省钱且能自己维护服务器的用 MinIO,功能体验几乎一样。


💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。

http://www.gsyq.cn/news/1606017.html

相关文章:

  • 吉阳区正宗椰子鸡推荐|符合海南本土特色的宝藏门店
  • 《AI抢产能致车规存储缺货?欣芯半导体给出eMMC/UFS“供应韧性”破局与选型指南》
  • 如何甄选靠谱展厅设计公司:从效果到落地的实战指南
  • 2026去水印不破坏原图的方法!电脑手机在线无痕去水印工具+PS教程
  • Java计算机毕设之基于 Java 的部门通知与任务一体化管理系统 团队协作型任务分配管理系统(完整前后端代码+说明文档+LW,调试定制等)
  • 2026奶茶店收银系统维护商推荐解析:凤梨收银系统适配茶饮业态的专业参考
  • 专业的杭州Geo哪家有实力
  • 查询优化-提升子查询-UNION类型
  • STM32和STM32CubeMX实现SHT30温湿度传感器 保姆级教程
  • 社区公益服务平台 Java+SpringBoot+Vue 前后分离
  • 营销智能体选哪个?一份基于实际场景的对比指南正在改变内容生成、投放优化和用户互动的效率。但市场上的产品形态差异很大:有的只是套了壳的通用写作工具,有的是传统营销 SaaS 加了个 AI 入口。选错不仅
  • 口碑佳的智能产品有何奥秘
  • 收藏!AI应用开发路线图:Java后端+Python大模型,小白也能轻松入门并快速上手
  • 软件数据可视化化的图表展示与交互
  • 暑假将至,校园安防不“放假”:国标GB28181视频监控平台EasyCVR这套视频融合方案让安全“全年无休”
  • [百度网盘] 大模型AI应用开发企业级项目实战(提示词工程+大模型NLP应用+AI对话产品)
  • 数据库巡检怎么做?Prometheus+Grafana监控体系搭建指南
  • 记一次由「系统Swap空间」被频繁使用导致的性能急剧下降
  • 软件检测实验室CMA资质认定技术人员和管理人员岗位要求与职责划分
  • GPT-5.6震撼来袭!OpenAI开启智能体基础设施时代,跑分已不重要!
  • 快速集成脑筋急转弯API:用Python构建你的命令行问答游戏
  • MSPM0 SYSCTL模块深度解析:时钟与功耗管理实战指南
  • 16 CFR 1640软垫家具阻燃
  • 从后厨到前台:一家连锁餐企如何用三年时间完成合同管理的数字化重构
  • 5款热门有声书软件实测,哪款最适合你?
  • 操作系统内存分配:伙伴系统与Slab分配器的结合
  • 【ChatGPT API成本控制实战手册】:20年架构师亲授7大隐形计费陷阱与精准预算建模法
  • 微信小程序性能优化:首屏加载与渲染提速指南
  • 20人研发团队MacBook选型找谁咨询
  • Java毕设选题推荐:基于 Java 的上下级任务对接管理平台设计与开发 轻量化企业任务审批与跟踪管理系统设计实现【附源码、mysql、文档、调试+代码讲解+全bao等】