背景
做外包项目经常遇到文件上传需求------用户头像、合同PDF、Excel导出文件。一开始图省事用阿里云OSS,结果有个项目用户上传了几千份合同扫描件,一个月OSS费用干到200多。
200块不算多,但积少成多,三个客户的项目加起来一年几千块。而我的服务器本来就剩200G磁盘闲着,于是全部切到MinIO自建。
MinIO是什么
MinIO是开源的对象存储,兼容Amazon S3 API。部署在你的服务器上,Docker一个容器搞定。代码里原来怎么写S3,现在就怎么写MinIO------API一模一样。
Docker部署
bash
services:
minio:
image: minio/minio:latest
container_name: minio
ports:
- "9000:9000" # API端口
- "9001:9001" # 控制台端口
volumes:
- ./minio/data:/data
- ./minio/config:/root/.minio
environment:
- MINIO_ROOT_USER=admin
- MINIO_ROOT_PASSWORD=your_password_here
command: server /data --console-address ":9001"
networks:
- aipdt-cloud
restart: unless-stopped
启动后用 http://你的IP:9001 登录控制台,创建Bucket。建议至少建两个:public 放公开文件(头像之类的),private 放私密文件(合同、证件)。
SpringBoot接入
依赖就一个:
xml
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.7</version>
</dependency>
配置文件:
vbnet
minio:
endpoint: http://minio:9000 # 容器内直接用服务名
access-key: admin
secret-key: your_password_here
bucket-public: public
bucket-private: private
配置类:
kotlin
@Configuration
public class MinioConfig {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.access-key}")
private String accessKey;
@Value("${minio.secret-key}")
private String secretKey;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
}
工具类
把常用操作封装一个工具类,业务代码里一行搞定:
typescript
@Slf4j
@Component
public class MinioUtil {
@Autowired
private MinioClient minioClient;
// 上传文件
public String upload(String bucket, InputStream inputStream,
String fileName, long size) {
try {
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucket)
.object(fileName)
.stream(inputStream, size, -1)
.contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)
.build());
return fileName;
} catch (Exception e) {
log.error("上传失败", e);
throw new RuntimeException("文件上传失败");
}
}
// 获取文件流
public InputStream download(String bucket, String fileName) {
try {
return minioClient.getObject(GetObjectArgs.builder()
.bucket(bucket)
.object(fileName)
.build());
} catch (Exception e) {
log.error("下载失败", e);
throw new RuntimeException("文件下载失败");
}
}
// 生成临时访问链接(7天有效)
public String presignedUrl(String bucket, String fileName) {
try {
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.bucket(bucket)
.object(fileName)
.expiry(7, TimeUnit.DAYS)
.method(Method.GET)
.build());
} catch (Exception e) {
log.error("生成链接失败", e);
return null;
}
}
// 删除文件
public void delete(String bucket, String fileName) {
try {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(bucket)
.object(fileName)
.build());
} catch (Exception e) {
log.error("删除失败", e);
}
}
}
业务代码示例
上传头像:
less
@PostMapping("/avatar")
public Result<String> uploadAvatar(@RequestParam MultipartFile file) {
String fileName = "avatar/" + userId + ".jpg";
minioUtil.upload(bucketPublic, file.getInputStream(),
fileName, file.getSize());
String url = minioUtil.presignedUrl(bucketPublic, fileName);
return Result.ok(url);
}
导出Excel存MinIO:
ini
// 生成Excel
ByteArrayOutputStream out = new ByteArrayOutputStream();
excelWriter.write(out);
// 上传到MinIO
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
String fileName = "excel/" + UUID.randomUUID() + ".xlsx";
minioUtil.upload(bucketPrivate, in, fileName, out.size());
// 返回下载链接
String downloadUrl = minioUtil.presignedUrl(bucketPrivate, fileName);
访问策略
公开文件:头像、封面图等,Bucket设置为公开读:
arduino
mc policy set download myminio/public
私密文件:合同、身份证等,Bucket保持私有,通过 presigned URL 控制访问,链接带过期时间和签名,安全可控。
踩过的坑
1. 容器内用localhost连不上
MinIO和SpringBoot都在Docker里时,endpoint 不能用 localhost。配容器名 http://minio:9000,或用宿主机IP。
2. 上传大文件OOM
putObject 默认把整个文件读进内存。上传几十M的文件时内存会爆。换成分片上传:
less
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucket)
.object(fileName)
.stream(inputStream, -1, 5 * 1024 * 1024) // 5M分片
.build());
第三个参数 -1 表示未知总大小,第四个参数 5M 是分片大小。适合前端直传的场景。
3. presigned URL 的域名问题
MinIO生成的链接默认是 http://内网IP:9000/bucket/file。如果前端需要访问,链接里得是外网域名。配nginx代理:
bash
location /minio/ {
proxy_pass http://minio:9000/;
}
或者在MinIO启动时指定环境变量 MINIO_SERVER_URL=https://你的域名。
什么时候不适合MinIO
- 需要CDN加速:OSS自带CDN,MinIO得自己加一层nginx反向代理和CDN配置
- 高可用刚需:单机挂了就不可用。MinIO也支持集群模式,但你得搞多台服务器
- 合规要求高:比如等保三级要求对象存储有审计日志、跨区域备份,自建搞起来麻烦
以上三条都不满足的话,MinIO够用了。
总结
- MinIO替代OSS,Docker一键部署,S3 API兼容
- public/private两个Bucket,公开读+预签名URL两种访问模式
- 大文件用分片上传,避免OOM
- 对CDN和高可用没要求的中小型项目,MinIO完全够用
- 一年省几千块OSS费用,一台服务器资源充分利用
我正以"一个人+AI"模式做独立开发,微信搜索小程序"面狮狮"体验AI模拟面试。更多实战经验:gitee.com/yao113088/j...