minio分片的Etag(md5)计算方式
研究minio分片计算etag(md5)的方式,给前端本地验证文件是否已上传提供思路
·
参考:
- https://github.com/minio/minio/blob/master/internal/etag/etag.go
- https://gitee.com/lxp135/minio-plus/issues/I9VNCN
- https://gitee.com/lxp135/minio-plus/issues/IA5ECM
应用:
- 前端能够先在本地通过这种方式计算md5,调用服务器的接口,判断文件是否存在,是否需要上传。或者在上传完成后,比较前端算的md5和后端返回的md5是否一致,即可以校验本地文件校验完整性
- 后端能够统一计算md5的方式,防止一个应用直接上传文件到minio,但在数据库记录时用minio中的etag数据作为md5,导致在本地上传一个相同的文件,由于计算方式不同导致再次上传一个相同的文件
- 这种方式能够防止合并时,重新去获取所有的分片数据重新计算md5导致性能的损耗
原理:
- 普通上传:这个etag就是MD5
- 此时一定是整个文件上传,PutObjectArgs.builder().stream(in, in.available(), -1 )
- 而不是指定大小进行上传才行(),如”PutObjectArgs.builder().stream(in, in.available(), 10 * 1024 * 1024 ) // 以每10m大小进行分片上传“ 这种方式则底层还是用的是分片上传。但当不满足分片大小时,etag的结果就和普通用md5一样
- 而一般编程中显示调用分片上传只是为了让分片上传过程可暂停且可感知进度
- 分片上传:
- Minio会对每个分片文件单独计算md5,最终合并时通过这些md5转换成byte[]并进行汇总,再计算出最终合并文件的md5(带上分片文件数)作为etag (而不用读取整个文件流再进行计算),结构为EATG值-分片数。
- 分片数从1开始
- 额外: MinIO 在分片上传时会提前在磁盘创建出空洞文件(也可以叫稀疏文件),在合并文件时,并没有实际上的磁盘IO读写,所以合并分片是一个安全操作。
简单代码实例:
import cn.hutool.core.io. FileUtil;
import cn.hutool.core.io. IoUtil;
import cn.hutool.core.util. HexUtil;
import cn.hutool.crypto.SecureUtil;
import java.io.BufferedInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args){
String filePath = "C:\\hello.jpg"; // 文件最好大于5m,这个minio默认且最小的分片大小
int chunkSize = 5242880; // 5m ( 5 * 1024 * 1024 ) 进行分块
List<byte[]> list = new ArrayList<>();
// 1. 计算每个分片的大小
byte[] buffer = new byte[chunkSize];
int chunkIndex = 0; // 当前分块
try(BufferedInputStream bufferedInputStream = new BufferedInputStream(Files.newInputStream(Paths.get(filePath)), chunkSize)){
int bytesRead; // 读取的数据大小
while((bytesRead = bufferedInputStream.read(buffer, 0, chunkSize)) != -1){
++chunkIndex;
byte[] buffer3 = new byte[bytesRead]; // 实际读取的数据,不能用buffer,因为可读取的数据可能小于buffer的空间大小
System.arraycopy(buffer, 0 , buffer3, 0, bytesRead); // 数组复制
// 只要是计算md5的方式都行,不一定用hutool的方式
String md5 = SecureUtil.md5(IoUtil.toStream(buffer3)); // 计算当前读取的分块的md5
System.out.println("当前分块:" + chunkIndex + " md5 " + md5);
byte[] partByte = HexUtil.decodeHex(md5); // 将md5重新转换为数组
list.add(partByte);
}
}catch(Exception ex){
throw new RuntimeException(ex);
}
// 2. 通过每个分片计算得到的md5, 计算合并后的整个文件的md5
int size = 0;
for(byte[] bytes : list){
size += bytes.length;
}
byte[] result = new byte[size];
int start = 0;
for(byte[] bytes : list){
System.arraycopy(bytes, 0, result, start, bytes.length);
start += bytes.length;
}
System.out.println("转义字节数组后拼接并计算MD5 = " + SecureUtil.md5(IoUtil.toStream(result)) + "-" + chunkIndex);
}
}
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)