基于Docker部署MinIO并在Spring Boot中集成对象存储服务
容器化部署MinIO服务端
镜像获取与准备
从官方仓库获取最新稳定版镜像:
docker pull minio/minio:latest
数据持久化配置
创建宿主机目录用于映射容器内的存储与配置:
mkdir -p /opt/minio/{storage,config}
服务启动参数
执行以下命令启动容器,注意端口映射与环境变量配置:
docker run -d \
--name minio-server \
--restart=unless-stopped \
-p 9000:9000 \
-p 9090:9090 \
-v /opt/minio/storage:/data \
-v /opt/minio/config:/root/.minio \
-e MINIO_ROOT_USER=admin \
-e MINIO_ROOT_PASSWORD=SecurePass123 \
minio/minio server /data --console-address ":9090"
参数说明:
9000:S3 API服务端口9090:Web管理控制台端口MINIO_ROOT_USER:管理员账号MINIO_ROOT_PASSWORD:管理员密码(需8位以上)
访问验证
浏览器打开 http://服务器IP:9090,使用配置的账号密码登录管理界面。
自启设置
docker update --restart=always minio-server
Spring Boot工程集成
依赖引入
在pom.xml中添加必要组件:
<dependencies>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.7</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
</dependencies>
配置属性类
定义YAML配置映射:
@Data
@Component
@ConfigurationProperties(prefix = "oss.minio")
public class MinioServerProps {
private String endpoint;
private String accessKey;
private String secretKey;
private String defaultBucket;
}
客户端初始化
@Configuration
public class MinioClientFactory {
@Autowired
private MinioServerProps serverProps;
@Bean
public MinioClient createClient() {
return MinioClient.builder()
.endpoint(serverProps.getEndpoint())
.credentials(serverProps.getAccessKey(), serverProps.getSecretKey())
.build();
}
}
业务实现层
文件元信息封装:
@Data
public class ObjectMeta {
private String objectKey;
private Long size;
private LocalDateTime lastModified;
private Boolean isFolder;
}
核心服务实现:
@Service
public class StorageServiceImpl implements StorageService {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioServerProps serverProps;
@Override
public List<ObjectMeta> listObjects(String prefix) {
List<ObjectMeta> result = new ArrayList<>();
try {
Iterable<Result<Item>> objects = minioClient.listObjects(
ListObjectsArgs.builder()
.bucket(serverProps.getDefaultBucket())
.prefix(prefix)
.recursive(true)
.build());
for (Result<Item> itemResult : objects) {
Item item = itemResult.get();
ObjectMeta meta = new ObjectMeta();
meta.setObjectKey(item.objectName());
meta.setSize(item.size());
meta.setLastModified(item.lastModified().toLocalDateTime());
meta.setIsFolder(item.isDir());
result.add(meta);
}
} catch (Exception e) {
throw new RuntimeException("列举对象失败", e);
}
return result;
}
@Override
public String uploadStream(MultipartFile file, String category) {
String originalName = file.getOriginalFilename();
String ext = originalName.substring(originalName.lastIndexOf("."));
String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
String key = String.format("%s/%s/%s%s",
category, datePath, UUID.randomUUID().toString().replace("-", ""), ext);
try (InputStream stream = file.getInputStream()) {
minioClient.putObject(PutObjectArgs.builder()
.bucket(serverProps.getDefaultBucket())
.object(key)
.stream(stream, file.getSize(), 10 * 1024 * 1024)
.contentType(file.getContentType())
.build());
} catch (Exception e) {
throw new RuntimeException("上传失败", e);
}
return serverProps.getEndpoint() + "/" + serverProps.getDefaultBucket() + "/" + key;
}
@Override
public void fetchToResponse(String objectKey, HttpServletResponse resp) {
try (InputStream stream = minioClient.getObject(GetObjectArgs.builder()
.bucket(serverProps.getDefaultBucket())
.object(objectKey)
.build())) {
StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder()
.bucket(serverProps.getDefaultBucket())
.object(objectKey)
.build());
resp.setContentType(stat.contentType());
resp.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(objectKey, StandardCharsets.UTF_8));
StreamUtils.copy(stream, resp.getOutputStream());
} catch (Exception e) {
throw new RuntimeException("下载失败", e);
}
}
@Override
public void removeObject(String objectKey) {
try {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(serverProps.getDefaultBucket())
.object(objectKey)
.build());
} catch (Exception e) {
throw new RuntimeException("删除失败", e);
}
}
}
YAML配置示例
oss:
minio:
endpoint: http://192.168.1.100:9000
access-key: admin
secret-key: SecurePass123
default-bucket: app-files