初识Minio
Minio官方文档地址:https://min.io/
最近公司做了一个分布式项目,文档存储这块儿,选型没选 FasDFS ,而选了Minio。自己下来查找了一些资料对比了一下:
1.安装部署(运维)复杂度
我记得以前用FastDFS的时候,我自己下去试着去部署了一下,特别复杂,什么 Tracker和Storage的关系,主从,完了当时还部署失败了
Minio官方文档提供出来了部署方式:
Windows:
Linux:
(其他请参考官方文档…)
2.就是Minio有官方文档,FastDFS没有
(你看这个文档,看着就清晰、舒服)
3.UI界面
FastDFS是没有一个界面的,而Minio带了一个很友好的操作界面
4.MinIO是一款先进的开源的OSS存储服务(官网这么介绍)。与其他存储从基础上就不一样,MinIO是为了高性能和亚马逊S3的API而开发的。适合大数据量、高负载的场景。
Spring集成Minio
1.引入依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>3.0.10</version>
</dependency>
2.Minio提供了一个Client调用服务端操作文件的,但我们还可以再封装一下:
/**
* minio template
* @author Jarvis
* @date 2020/1/26 16:06
*/
@AllArgsConstructor
public class MinioTemplate {
private String endpoint;
private String accessKey;
private String secretKey;
private int partSize;
private MinioClient client;
@SneakyThrows
public MinioTemplate(String endpoint, String accessKey, String secretKey, int partSize) {
this.endpoint = endpoint;
this.accessKey = accessKey;
this.secretKey = secretKey;
this.partSize = partSize;
this.client = MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
/**
* 创建bucket
*
* @param bucketName bucket名称
*/
public void createBucket(String bucketName) throws Exception {
if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
}
/**
* 获取全部bucket
* <p>
* https://docs.minio.io/cn/java-client-api-reference.html#listBuckets
*/
public List<Bucket> getAllBuckets() throws Exception {
return client.listBuckets();
}
/**
* 根据bucketName获取信息
* @param bucketName bucket名称
*/
public Optional<Bucket> getBucket(String bucketName) throws Exception {
return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
}
/**
* 根据bucketName删除信息
* @param bucketName bucket名称
*/
public void removeBucket(String bucketName) throws Exception {
client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}
/**
* 分区上传文件
* @param bucketName bucket名称
* @param objectName 文件名称
* @param stream 文件流
* @param size 文件大小
*/
public String putObject(String bucketName, String objectName, InputStream stream, Long size) throws Exception{
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(stream, size, partSize)
.build();
ObjectWriteResponse objectWriteResponse = client.putObject(putObjectArgs);
return objectWriteResponse.object();
}
/**
* 根据文件前置查询文件
*
* @param bucketName bucket名称
* @param prefix 前缀
* @param recursive 是否递归查询
* @return MinioItem 列表
*/
public List<Item> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) throws Exception {
List<Item> objectList = new ArrayList<>();
ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder()
.bucket(bucketName)
.prefix(prefix)
.recursive(recursive)
.build();
Iterable<Result<Item>> objectsIterator = client
.listObjects(listObjectsArgs);
while (objectsIterator.iterator().hasNext()) {
objectList.add(objectsIterator.iterator().next().get());
}
return objectList;
}
/**
* 获取文件外链
* 这里的 method 方法决定最后链接是什么请求获得
* expiry 决定这个链接多久失效
* @param bucketName bucket名称
* @param objectName 文件名称
* @return url
*/
public String getObjectURL(String bucketName, String objectName) throws Exception {
GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
.bucket(bucketName)
.method(Method.GET)
.expiry(7, TimeUnit.DAYS)
.object(objectName)
.build();
return client.getPresignedObjectUrl(args);
}
/**
* 获取文件
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @return 二进制流
*/
public InputStream getObject(String bucketName, String objectName) throws Exception {
GetObjectArgs getObjectArgs = GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build();
return client.getObject(getObjectArgs);
}
/**
* 上传文件 base64
* @param bucketName bucket名称
* @param objectName 文件名称
* @param base64Str 文件base64
*/
public String putObject(String bucketName, String objectName, String base64Str) throws Exception{
InputStream inputStream = new ByteArrayInputStream(base64Str.getBytes());
// 进行解码
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] byt = new byte[0];
try {
byt = base64Decoder.decodeBuffer(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
inputStream = new ByteArrayInputStream(byt);
putObject(bucketName, objectName, inputStream, Long.valueOf(byt.length));
return objectName;
}
/**
* 上传文件
* @param bucketName bucket名称
* @param objectName 文件名称
* @param file 文件
* @throws Exception
*/
public String putObject( String bucketName,String objectName, MultipartFile file) throws Exception{
this.putObject(bucketName, objectName, file.getInputStream(), file.getSize());
return objectName;
}
/**
* 获取文件信息
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject
*/
public ObjectStat getObjectInfo(String bucketName, String objectName) throws Exception {
StatObjectArgs statObjectArgs = StatObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build();
return client.statObject(statObjectArgs);
}
/**
* 删除文件
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject
*/
public void removeObject(String bucketName, String objectName) throws Exception {
client.removeObject(bucketName, objectName);
}
/**
* 根据文件名返回对应contentType
* @param objectName
* @return
*/
private String getContentType(String objectName) {
if(FileNameUtil.isPicture(objectName)) {
return "image/jpeg";
}
if(FileNameUtil.isVideo(objectName)) {
return "video/mp4";
}
return null;
}
/**
* 获取直传链接
* @param bucketName bucket名称
* @param objectName 文件名称
* @throws Exception
*/
public String presignedPutObject( String bucketName,String objectName) throws Exception{
GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
.method(Method.PUT)
.bucket(bucketName)
.object(objectName)
.expiry(7, TimeUnit.DAYS)
.build();
return client.getPresignedObjectUrl(getPresignedObjectUrlArgs);
}
/**
* 合并文件
* @param bucketName
* @param chunkNames
* @param targetObjectName
* @return
* @throws Exception
*/
public String composeObject(String bucketName, List<String> chunkNames, String targetObjectName) throws Exception{
List<ComposeSource> sources = new ArrayList<>(chunkNames.size());
for (String chunkName : chunkNames) {
ComposeSource composeSource = ComposeSource.builder()
.bucket(bucketName)
.object(chunkName)
.build();
sources.add(composeSource);
}
ComposeObjectArgs composeObjectArgs = ComposeObjectArgs.builder()
.bucket(bucketName)
.sources(sources)
.object(targetObjectName)
.build();
ObjectWriteResponse objectWriteResponse = client.composeObject(composeObjectArgs);
return objectWriteResponse.object();
}
}
然后写一个配置类,把这个 MinioTemplate 交给Spring管理
/**
* @author Jarvis
* @date 2020/1/26 11:38
*/
@Configuration
public class MinioConfig {
@Bean
public MinioTemplate minioTemplate(@Value("${minio.endPoint}") String endPoint, @Value("${minio.accessKey}") String accessKey,
@Value("${minio.secretKey}") String secretKey, @Value("${minio.partSize}") int partSize) throws MinioException {
return new MinioTemplate(endPoint, accessKey, secretKey, partSize);
}
}
Yaml配置:
#minio
minio:
endPoint: http://localhost:minio
accessKey: root
secretKey: root
bucketName: minioTest
partSize: 104857600
对Minio的理解就到这儿了…