MinIO

MinIO学习笔记

分布式文件系统:MinIO 是一个非常轻量的服务,可以很简单的和其他应用的结合使用,它兼容亚马逊 S3 云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等。

它一大特点就是轻量,使用简单,功能强大,支持各种平台,单个文件最大5TB,兼容 Amazon S3接口,提供了 Java、Python、GO等多版本SDK支持。

Java Quickstart Guide — MinIO Object Storage for Linux

一、导入

1、安装MinIO

在这里可以指定用户名密码,以及控制台地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#在用户(root)目录下创建文件夹
mkdir -p ~/minio/data
#运行docker指令
docker run \
-d
-p 9000:9000 \
-p 9001:9001 \
--name minio \
-v ~/minio/data:/data \
-e "MINIO_ROOT_USER=minioadmin" \
-e "MINIO_ROOT_PASSWORD=minioadmin" \
quay.io/minio/minio:RELEASE.2022-09-07T22-25-02Z /data --console-address ":9001"

docker run \
-d \
-p 9000:9000 \
-p 9200:9200 \
--name minio \
-v ~/minio/data:/data \
-e "MINIO_ROOT_USER=minioadmin" \
-e "MINIO_ROOT_PASSWORD=minioadmin" \
quay.io/minio/minio server /data --console-address ":9200"

2、使用MinIO

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--        minio依赖-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.14</version>
</dependency>

<!-- okhttp发送http请求-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.8.1</version>
</dependency>

3、创建bucket

在此之前要先创建一个bucket,以存储我们上传的文件。

  1. 通过控制台创建

  2. 通过代码创建

    1
    2
    3
    4
    5
    6
    7
    boolean found =minioClient.bucketExists(BucketExistsArgs.builder().bucket("testbucket").build());
    if (!found) {
    // 创建一个名为 'testbucket' 的新存储桶。
    minioClient.makeBucket(MakeBucketArgs.builder().bucket("testbucket").build());
    } else {
    System.out.println("Bucket 'testbucket' already exists.");
    }

4、上传文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class MinioTest {

//连接minio
static MinioClient minioClient =
MinioClient.builder()
.endpoint("http://192.168.101.129:9000/") //minio服务地址
.credentials("minioadmin", "minioadmin") //账号密码
.build();
//上传文件
@Test
public void upload() {
try {
//创建一个上传对象参数
UploadObjectArgs testbucket = UploadObjectArgs.builder()
.bucket("testbucket") //指定桶名
.object("001/test001.mp4")//指定桶内存储路径以及存储对象名
.filename("C:\\Users\\Lenovo\\Desktop\\upload\\1mp4.temp") //本地文件名
.contentType("video/mp4")//默认根据扩展名确定文件内容类型,也可以指定
.build();
minioClient.uploadObject(testbucket);
System.out.println("上传成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("上传失败");
}
}
}

5、删除文件

1
2
3
4
5
6
7
8
9
10
11
12
13
//删除文件
@Test
public void delete(){
try {
//创建一个移出对象参数
minioClient.removeObject(
RemoveObjectArgs.builder().bucket("testbucket").object("001/test001.mp4").build());
System.out.println("删除成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("删除失败");
}
}

6、查询并获取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//查询文件
@Test
public void getFile() {
//创建一个 获取对象参数
GetObjectArgs getObjectArgs = GetObjectArgs.builder().bucket("testbucket").object("001/test001.mp4").build();
try(
//获取输入流,将文件内容输入到内存中
FilterInputStream inputStream = minioClient.getObject(getObjectArgs);
//创建一个输出流,输出内存内容
FileOutputStream outputStream = new FileOutputStream(new File("C:\\Users\\Lenovo\\Desktop\\upload\\1_2.mp4"));
) {
//将输入流内容复制到输出流
IOUtils.copy(inputStream,outputStream);
} catch (Exception e) {
e.printStackTrace();
}
}

7、验证文件完整性。

这里实际上会将上传成功的文件再次下载下来。在本地比较文件的md5是否相同。然后返回结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Test
public void Integrity() {
//先上传
try {
UploadObjectArgs testbucket = UploadObjectArgs.builder()
.bucket("testbucket")
// .object("test001.mp4")
.object("testIntegrity/001.mp4")//添加子目录
.filename("C:\\Users\\Lenovo\\Desktop\\upload\\1mp4.temp")
.contentType("video/mp4")//默认根据扩展名确定文件内容类型,也可以指定
.build();
minioClient.uploadObject(testbucket);
System.out.println("上传成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("上传失败");
}
//再下载
GetObjectArgs getObjectArgs = GetObjectArgs.builder().bucket("testbucket").object("testIntegrity/001.mp4").build();
try(
FilterInputStream inputStream = minioClient.getObject(getObjectArgs);
FileOutputStream outputStream = new FileOutputStream(new File("C:\\Users\\Lenovo\\Desktop\\upload\\test1.mp4"));
) {
IOUtils.copy(inputStream,outputStream);
} catch (Exception e) {
e.printStackTrace();
}
//比较完整性
try{
FileInputStream fileInputStream1 = new FileInputStream(new File("C:\\Users\\Lenovo\\Desktop\\upload\\1mp4.temp"));
String source_md5 = DigestUtils.md5Hex(fileInputStream1);
FileInputStream fileInputStream = new FileInputStream(new File("C:\\Users\\Lenovo\\Desktop\\upload\\test1.mp4"));
String local_md5 = DigestUtils.md5Hex(fileInputStream);
if(source_md5.equals(local_md5)){
System.out.println("文件md5值相同");
}else{
System.out.println("文件md5值不同,疑似上传失败");
}
System.out.println("source_md5 = " + source_md5);
System.out.println("local_md5 = " + local_md5);
}catch (Exception e){
e.printStackTrace();
}

}

二、前置工作

1、前置工作

  • 创建一个mediafiles桶

  • 将创建的桶设为public

2、MinioConfig

application.yaml

1
2
3
4
5
6
7
minio:
endpoint: http://192.168.101.129:9000
accessKey: minioadmin
secretKey: minioadmin
bucket:
files: mediafiles
videofiles: video

配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
public class MinioConfig {


@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;

@Bean
public MinioClient minioClient() {

MinioClient minioClient =
MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
return minioClient;
}
}

然后通过MinioClient上传文件即可

三、MinIOUtil

1、前端简单的html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>上传Demo</title>
</head>

<body>
<h2>文件上传</h2>

<!-- 文件上传表单 -->
<form action="http://localhost:8080/upload/testUtil" method="POST" enctype="multipart/form-data">
<label for="filedata">选择文件:</label>
<input type="file" name="filedata" id="filedata" required><br><br>
<button type="submit">上传文件</button>
</form>
</body>

</html>

2、后端代码(图片上传)

先导入这3个依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--        minio依赖-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.14</version>
</dependency>

<!-- okhttp发送http请求-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.8.1</version>
</dependency>

<!--根据扩展名取mimetype-->
<dependency>
<groupId>com.j256.simplemagic</groupId>
<artifactId>simplemagic</artifactId>
<version>1.17</version>
</dependency>

Controller样例

1
2
3
4
5
6
7
8
9
10
11
@PostMapping("/testUtil")
public String upload2(MultipartFile filedata) throws IOException {

String upload = MinIOUtil.upload(filedata);

if(upload==null){
return "上传文件失败";
}else{
return "上传文件成功:"+upload;
}
}

使用封装好的MinIOUtil上传文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
public class MinIOUtil {

//MinIO服务器ip
static private String endpoint = "http://192.168.101.129:9000";
//账号accessKey
static private String accessKey = "minioadmin";
//密码secretKey
static private String secretKey = "minioadmin";
//传的桶名
static private String filesBucket ="mediafiles";

static private MinioClient minioClient;

static public String upload(MultipartFile filedata) throws IOException {

minioClient = MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();

//创建临时文件
File tempFile = File.createTempFile("minio", ".temp");
filedata.transferTo(tempFile);

//得到临时文件绝对路径
String absolutePath = tempFile.getAbsolutePath();


//得到原始文件名
String originalFilename = filedata.getOriginalFilename();
//根据后缀名得到mimetype
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
String mimeType = getMimeType(extension);


//通过时间戳获取一个路径
String defaultFolderPath = getDefaultFolderPath();

//获得文件md5
String fileMd5 = getFileMd5(tempFile);

//得到objectName
String objectName = defaultFolderPath+fileMd5+extension;

boolean b = addMediaFilesToMinIO(absolutePath, mimeType, filesBucket, objectName);

if(!b){
return null;
}else{
//文件路径
return endpoint+"/"+filesBucket+"/"+objectName;
}
}

//将文件上传到minio
static private boolean addMediaFilesToMinIO(String localFilePath, String mimeType, String bucket, String objectName) {
try {
//上传文件的参数信息
UploadObjectArgs testbucket = UploadObjectArgs.builder()
.bucket(bucket)//确定桶
.filename(localFilePath)//指定本地文件路径
.object(objectName) //包含完整路径
.contentType(mimeType)
.build();
minioClient.uploadObject(testbucket);
// log.debug("上传文件到minio成功:bucket:{},objectName:{}", bucket, objectName);
return true;
} catch (Exception e) {
e.printStackTrace();
// log.error("上传文件出错:bucket:{},objectName:{},错误信息:{}", bucket, objectName, e.getMessage());
}
return false;
}

//根据扩展名获取mimeType
static private String getMimeType(String extension) {
if(extension==null)
extension = "";
//通过扩展名得到媒体资源类型 mimeType
//根据扩展名取出mimeType
ContentInfo extensionMatch = ContentInfoUtil.findExtensionMatch(extension);
String mimeType = MediaType.APPLICATION_OCTET_STREAM_VALUE;//通用mimeType,字节流
if (extensionMatch != null) {
mimeType = extensionMatch.getMimeType();
}
return mimeType;
}

//获取文件的md5
static private String getFileMd5(File file) {
try (FileInputStream fileInputStream = new FileInputStream(file)) {
String fileMd5 = DigestUtils.md5Hex(fileInputStream);
return fileMd5;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

//获取文件默认存储目录路径 年/月/日,包含最后的'/'
static private String getDefaultFolderPath() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String folder = sdf.format(new Date()).replace("-", "/")+"/";
return folder;
}

}

upload方法执行成功后,会得到上传文件链接

四、断点续传

上传或下载任务中,将需要上传或者下载的文件,人为分成多个部分,分别上传和下载。如果遇到网络故障,可以从中断的地方继续上传或者下载。

  • 前端上传前先把文件分成块

  • 一块一块的上传,上传中断后重新上传,已上传的分块则不用再上传

  • 各分块上传完成最后在服务端合并文件

1、文件本地分块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Test
public void testChunk() throws IOException {
//本地文件路径
File sourseFile = new File("C:\\Users\\Lenovo\\Desktop\\upload\\1a.mp4");
//分块文件存储路径
String chunkFilePath = "C:\\Users\\Lenovo\\Desktop\\upload\\chunk\\";
//分块文件大小
int chunkSize = 1024*1024 * 5;
//分块文件个数
int chunkNum =(int)Math.ceil(sourseFile.length() * 1.0 / chunkSize) ;


//使用流从源文件读数据,向分块文件中写数据。
RandomAccessFile raf_r = new RandomAccessFile(sourseFile, "r");

//缓存区
byte[] bytes = new byte[1024];
for (int i = 0; i < chunkNum; i++) {

//表示创建一个分块文件
File chunkFile = new File(chunkFilePath + i);

//向分块文件写入流
RandomAccessFile raf_rw = new RandomAccessFile(chunkFile, "rw");
int len = -1;
//raf_r.read(bytes)读取源文件中的数据,存入bytes数组中,并将读取的长度返回。
//byte数组的长度多少,就读取多少。raf_r会自动维护读取到了哪。
//读完返回-1。
while((len=raf_r.read(bytes))!=-1){
//将bytes中的数据写入分块文件中
raf_rw.write(bytes,0,len);
if(chunkFile.length()>=chunkSize){
break;
}
}
//关闭流很重要
raf_rw.close();
}
//关闭流很重要
raf_r.close();

}

2、测试本地合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Test
public void testMerge() throws IOException {
//源文件
File sourseFile = new File("C:\\Users\\Lenovo\\Desktop\\upload\\1a.mp4");

//分块文件路径
File chunkFolder = new File("C:\\Users\\Lenovo\\Desktop\\upload\\chunk\\");
//合并文件存储路径
File mergeFile = new File("C:\\Users\\Lenovo\\Desktop\\upload\\1a_merge.mp4");

//取出所有分块文件
File[] files = chunkFolder.listFiles();
//将数组转成list
List<File> fileList = Arrays.asList(files);

//按照文件名排序
Collections.sort(fileList, new Comparator<File>() {
@Override
public int compare(File o1, File o2) {
return Integer.parseInt(o1.getName())-Integer.parseInt(o2.getName());
}
});

//向合并文件写的流
RandomAccessFile raf_rw = new RandomAccessFile(mergeFile, "rw");
//缓冲区
byte[] bytes = new byte[1024];
//遍历分块文件,向合并文件写
for (File file : fileList) {
//读分块的流
RandomAccessFile raf_r = new RandomAccessFile(file, "r");
int len= -1;

while ((len=raf_r.read(bytes)) !=-1){
raf_rw.write(bytes,0,len);
}
raf_r.close();

}
raf_rw.close();

//合并完成后对合并的文件进行md5校验
FileInputStream fileInputStream_merge = new FileInputStream(mergeFile);
FileInputStream fileInputStream_source = new FileInputStream(sourseFile);

String md5_merge = DigestUtils.md5Hex(fileInputStream_merge);
String md5_source = DigestUtils.md5Hex(fileInputStream_source);

if(md5_merge.equals(md5_source)){
System.out.println("文件合并成功!");
}

}

MinIO

3、分块文件上传MinIO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void uploadChunk() throws Exception{
for(int i=0;i<3;i++){
//上传文件的参数信息
UploadObjectArgs testbucket = UploadObjectArgs.builder()
.bucket("testbucket")//确定桶
.filename("C:\\Users\\Lenovo\\Desktop\\upload\\chunk\\"+i)//指定本地文件路径
// .object("1.mp4")//对象名 单层子目录
.object("chunk/"+i)
.build();
//上传文件
minioClient.uploadObject(testbucket);
System.out.println("上传分块"+i+"成功");
}
}

4、通过minio的合并文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Test
public void testMerge()throws Exception{
//传统方式:
// List<ComposeSource> sources = new ArrayList<>();
//
// for(int i=0;i<13;i++){
// //指定分块信息
// ComposeSource composeSource = ComposeSource.builder()
// .bucket("testbucket")
// .object("chunk/"+i)
// .build();
//
// sources.add(composeSource);
// }

//新方式:合并分块完成将分块文件清除 利用stream流.
//得到分块源文件集合了
List<ComposeSource> sources = Stream.iterate(0, i -> ++i)
.limit(3)
.map(i -> ComposeSource.builder()
.bucket("testbucket")
.object("chunk/".concat(Integer.toString(i)))
.build())
.collect(Collectors.toList());

//指定合并后的objectName信息
ComposeObjectArgs testbucket = ComposeObjectArgs.builder()
.bucket("testbucket")
.sources(sources)
.object("merge01.mp4")
.build();
//合并文件,报错 size 1048576 must be greater than 5242880 , minio默认的分块大小为5m
minioClient.composeObject(testbucket);
}

5、删除minio中的分块文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Test
public void test_removeObjects(){
//合并分块完成将分块文件清除
List<DeleteObject> deleteObjects = Stream.iterate(0, i -> ++i)
.limit(3)
.map(i -> new DeleteObject("chunk/".concat(Integer.toString(i))))
.collect(Collectors.toList());

//批量删除
RemoveObjectsArgs removeObjectsArgs = RemoveObjectsArgs.builder().bucket("testbucket").objects(deleteObjects).build();
Iterable<Result<DeleteError>> results = minioClient.removeObjects(removeObjectsArgs);
results.forEach(r->{
DeleteError deleteError = null;
try {
//删除失败则会产生错误信息,需要获取错误信息,
deleteError = r.get();
} catch (Exception e) {
e.printStackTrace();
}
});
}

五、项目中的用法(断点续传)

  • 前端会将文件分块

  • 将一个个分块传到后端,上传到MinIO(上传分块)

  • 检查文件上传是否成功(检查分块文件是否存在)

  • 全部分块文件上传成功后,合并文件(合并文件)

  • 清除分块

1、查询文件是否存在

一般来说我们上传到minio的文件需要一个数据表来存放。

md5为id主键。这样查询是否上传过文件方便

查询文件是否已经上传过,如果上传过。就不需要再上传,也不需要再分块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public RestResponse<Boolean> checkFile(String fileMd5) {
//先查询数据库
MediaFiles mediaFiles = mediaFilesMapper.selectById(fileMd5);
if(mediaFiles != null){
//如果数据库有,再去查minio中有没有
String bucket = mediaFiles.getBucket();
String filePath = mediaFiles.getFilePath();

//如果数据库存在再查询minio
GetObjectArgs testbucket = GetObjectArgs.builder()
.bucket(bucket)
.object(filePath)
.build();

//得到输入流
try{
FilterInputStream inputStream = minioClient.getObject(testbucket);
if(inputStream !=null){
inputStream.close();
//文件已存在
return RestResponse.success(true);
}
}catch (Exception e){
e.printStackTrace();
//文件不存在
}
//文件已存在
}
return RestResponse.success(false);
}

2、上传分块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//得到分块文件的目录
private String getChunkFileFolderPath(String fileMd5) {
return fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/" + "chunk" + "/";
}

@Override
public RestResponse uploadChunk(String fileMd5, int chunk, String localChunkFilePath) {
//分块文件路径,实际上就是要上传到minio哪个路径
String chunkFilePath = getChunkFileFolderPath(fileMd5)+chunk;

String mimeType = getMimeType(null);
//将分块文件上传到minio
boolean b = addMediaFilesToMinIO(localChunkFilePath, mimeType, bucket_videofiles, chunkFilePath);

if(!b){
//上传失败
return RestResponse.validfail(false,"上传分块文件失败");
}
//上传成功
return RestResponse.success(true);
}

3、查询分块文件是否存在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public RestResponse<Boolean> checkChunk(String fileMd5, int chunkIndex) {
//根据MD5得到分块文件所在目录的路径
String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
//如果数据库存在再查询minio
GetObjectArgs testbucket = GetObjectArgs.builder()
.bucket(bucket_videofiles)
.object(chunkFileFolderPath+chunkIndex)
.build();
//得到输入流
try{
FilterInputStream inputStream = minioClient.getObject(testbucket);
if(inputStream !=null){
inputStream.close();
//文件已存在
return RestResponse.success(true);
}
}catch (Exception e){
e.printStackTrace();
//文件不存在
}
return RestResponse.success(false);

}

4、合并文件

  • 构建好sources然后合并分块

  • 下载合并好的文件,检验md5,验证完整性

  • 文件信息入数据库

  • 清除分块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@Override
public RestResponse mergechunks(Long companyId, String fileMd5, int chunkTotal, UploadFileParamsDto uploadFileParamsDto) {
//找到分块文件调用minio的sdk进行文件合并
//合并分块完成将分块文件清除 利用stream流

//分块文件所在目录
String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);

//找到了所有的分块文件
List<ComposeSource> sources = Stream.iterate(0, i -> ++i)
.limit(chunkTotal)
.map(i -> ComposeSource.builder()
.bucket(bucket_videofiles)
.object(chunkFileFolderPath+i)
.build())
.collect(Collectors.toList());

//源文件名称
String filename = uploadFileParamsDto.getFilename();
//根据源文件名称,拿到扩展名
String extension = filename.substring(filename.lastIndexOf("."));
//合并后文件的objectName
String objectName = getFilePathByMd5(fileMd5, extension);
//指定合并后的objectName信息
ComposeObjectArgs testbucket = ComposeObjectArgs.builder()
.bucket(bucket_videofiles)
.sources(sources)
.object(objectName)
.build();
//合并文件,报错 size 1048576 must be greater than 5242880 , minio默认的分块大小为5m
try{
minioClient.composeObject(testbucket);
}catch (Exception e){
e.printStackTrace();
log.error("合并文件出错,bucket:{},objectName:{},错误信息:{}",bucket_videofiles,objectName,e.getMessage());
return RestResponse.validfail(false,"合并文件异常");
}

//校验合并后的文件和源文件是否一致,视频才上传成功
//下载文件,得到临时文件
File file = downloadFileFromMinIO(bucket_videofiles, objectName);
//计算合并文件的md5
try {
FileInputStream inputStream = new FileInputStream(file);
String mergeFile_md5 = DigestUtils.md5Hex(inputStream);

if(!fileMd5.equals(mergeFile_md5)){
log.error("校验合并文件MD5值不一致,原始文件:{},合并文件:{}",fileMd5,mergeFile_md5);
return RestResponse.validfail(false,"文件校验失败");
}
//设置文件大小
uploadFileParamsDto.setFileSize(file.length());
} catch (IOException e) {
return RestResponse.validfail(false,"文件校验失败");
}

//将文件信息入库
MediaFiles mediaFiles = ProxyService.addMediaFilesToDb(companyId, fileMd5, uploadFileParamsDto, bucket_videofiles, objectName);
if(mediaFiles == null){
return RestResponse.validfail(false,"文件入库失败");
}
clearChunkFiles(chunkFileFolderPath,chunkTotal);
//清理分块文件
return RestResponse.success(true);
}

5、清除分块文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void clearChunkFiles(String chunkFileFolderPath,int chunkTotal){

try {
//构建deleteObjects集合
List<DeleteObject> deleteObjects = Stream.iterate(0, i -> ++i)
.limit(chunkTotal)
.map(i -> new DeleteObject(chunkFileFolderPath.concat(Integer.toString(i))))
.collect(Collectors.toList());

//批量删除
RemoveObjectsArgs removeObjectsArgs = RemoveObjectsArgs.builder().bucket("video").objects(deleteObjects).build();
Iterable<Result<DeleteError>> results = minioClient.removeObjects(removeObjectsArgs);
results.forEach(r->{
DeleteError deleteError = null;
try {
deleteError = r.get();
} catch (Exception e) {
e.printStackTrace();
log.error("清楚分块文件失败,objectname:{}",deleteError.objectName(),e);
}
});
} catch (Exception e) {
e.printStackTrace();
log.error("清楚分块文件失败,chunkFileFolderPath:{}",chunkFileFolderPath,e);
}
}