安装
Docker 部署 MinIO
拉取镜像
docker pull minio/minio --platform linux/amd64
创建挂载目录
mkdir /Users/clear/docker/data/minio/config
mkdir /Users/clear/docker/data/minio/data
启动
docker run \
-p 19000:9000 \
-p 9090:9090 \
--net=host \
--name minio \
-d --restart=always \
-e "MINIO_ACCESS_KEY=minioadmin" \
-e "MINIO_SECRET_KEY=minioadmin" \
-v /data/clear/docker/minio/data:/data \
-v /data/clear/docker/minio/config:/root/.minio \
minio/minio server \
/data --console-address ":9090" -address ":19000"
访问可视化页面
访问链接:http://ip:9090/login
账号密码:minioadmin
/minioadmin
使用
Spring 搭配开发
引入
implementation group: 'io.minio', name: 'minio', version: '7.1.0'
配置类与工具类
minio.ip=127.0.0.1
minio.port=9090
minio.access-key=minioadmin
minio.secret-key=minioadmin
@Configuration
public class MinioConfig {
@Value("${minio.ip}")
private String ip;
@Value("${minio.port}")
private int port;
@Value("${minio.access-key}")
private String accessKey;
@Value("${minio.secret-key}")
private String secretKey;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(ip, port, false) //https or not
.credentials(accessKey, secretKey)
.build();
}
}
@Component
public class MinioUtil {
private Logger logger = LoggerFactory.getLogger(MinioUtil.class);
@Autowired
private MinioClient minioClient;
/**
* 创建bucket
*/
private void createBucket(String bucketName) throws Exception {
BucketExistsArgs existsArgs = BucketExistsArgs.builder().bucket(bucketName).build();
if(!minioClient.bucketExists(existsArgs)) {
MakeBucketArgs makeArgs = MakeBucketArgs.builder().bucket(bucketName).build();
minioClient.makeBucket(makeArgs);
logger.info("bucket {} 不存在, 自动创建该bucket", bucketName);
}
}
/**
* 从给定输入流中传输对象并放入bucket
*/
public ObjectWriteResponse putObject(String bucketName, String objectName, InputStream stream, long objectSize, String contentType) throws Exception {
if (StringUtils.isEmpty(bucketName)) {
throw new RuntimeException("保存的bucketName为空");
}
createBucket(bucketName);
//long objSize = -1;
long partSize = -1; //objectSize已知,partSize设为-1意为自动设置
PutObjectArgs putArgs = PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(stream, objectSize, partSize)
.contentType(contentType)
.build();
ObjectWriteResponse response = minioClient.putObject(putArgs);
return response;
}
/**
* 从bucket获取指定对象的输入流,后续可使用输入流读取对象
* getObject与minio server连接默认保持5分钟,
* 每隔15s由minio server向客户端发送keep-alive check,5分钟后由客户端主动发起关闭连接
*/
public InputStream getObject(String bucketName, String objectName) throws Exception{
GetObjectArgs args = GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build();
return minioClient.getObject(args);
}
/**
* 获取对象的临时访问url,有效期5分钟
*/
public String getObjectURL(String bucketName, String objectName) throws Exception{
GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
.bucket(bucketName)
.object(objectName)
.expiry(5, TimeUnit.MINUTES)
.method(Method.GET)
.build();
return minioClient.getPresignedObjectUrl(args);
}
/**
* 删除对象
*/
public void removeObject(String bucketName, String objectName) throws Exception {
RemoveObjectArgs args = RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build();
minioClient.removeObject(args);
logger.info("bucket:{}文件{}已删除", bucketName , objectName);
}
/**
* 上传MultipartFile
* @param bucketName 文件存放的bucket
* @param filePath 文件在bucket里的全目录
*/
public ObjectWriteResponse uploadFile(String bucketName, String filePath, MultipartFile file) throws Exception{
return putObject(bucketName, filePath, file.getInputStream(), file.getSize(), file.getContentType());
}
/**
* 从minio下载文件,直接通过response传输
*/
public void downloadFile(String bucketName, String filePath, HttpServletResponse response) throws Exception {
try (InputStream is = getObject(bucketName, filePath);
BufferedInputStream bis = new BufferedInputStream(is);
OutputStream os = response.getOutputStream()) {
//try {
/*InputStream is = getObject(bucketName, filePath);
BufferedInputStream bis = new BufferedInputStream(is);
OutputStream os = response.getOutputStream();*/
response.setContentType("application/force-download;charset=utf-8");// 设置强制下载而不是直接打开
response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(filePath, "UTF-8"));// 设置文件名
byte[] buffer = new byte[1024*1024*1024]; //buffer 1M
int offset = bis.read(buffer);
while (offset != -1) {
os.write(buffer, 0, offset);
offset = bis.read(buffer);
}
os.flush();
} catch (Exception e) {
logger.error("下载文件失败"+e.getMessage(), e);
throw new RuntimeException("下载文件失败" , e);
}
}
}
记录一下几个问题:
SpringBoot 2.1.13.RELEASE 环境下集成了 MinIO 客户端 7.1.0 版本,最新的版本貌似集成不进去
文件下载也就是
minioClient.getObject(args)
场景下,客户端与 MinIO server 连接默认保持 5 分钟,每隔 15s 由 MinIO server 向客户端发送 keep-alive check,5 分钟后由客户端主动发起关闭连接。文件下载时使用流的方式传输,上述代码里使用
try-with-resource
的方式确保finally
关闭流,但是通过 wireshark 观测貌似直接普通try-catch
最后也没有造成连接的泄露,应该是底层的 okio 做了优化、5 分钟后直接发起了连接关闭。但作为好习惯还是应该主动去关闭流。