[技术干货] 如何计算普通对象及多段上传对象的ETag(MD5)

一、普通对象

上传对象时的Content-MD5值,OBS的API文档描述如下:

  content-md5.PNG

 在上传对象之前计算本地文件的MD5值比较简单,利用各种编程语言中自带的MD5库函数计算对象数据的MD5值即可,无须赘述,以下是Java的示例代码:

           FileInputStream in = new FileInputStream(new File("D:\\xxx.txt"));

           FileChannel channel = in.getChannel();

           MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, file.length());

           MessageDigest md = MessageDigest.getInstance("MD5");

           md.update(buffer);

           // 计算得到MD5值的二进制流

           byte[] digests = md.digest();

           // 对MD5值进行Base64编码

           return Base64.encode(digests);

这里需要注意的是,Base64编码是直接对计算得到的MD5值的二进制流进行编码,而不是将MD5值的二进制流转成16进制字符串之后再进行编码,常见的错误的计算方式如下:

          Base64.encode(bytes2Hex(digests).getBytes())

对象上传成功之后,对象的MD5值会通过ETag头域返回。后续也可以通过获取对象元数据的接口来查询ETag(MD5)。

二、多段上传方式上传的对象

根据OBS的资料描述,多段方式上传的对象,会在合并段的时候计算整个对象的ETag,但实际上这个ETag并不是直接计算整个对象数据的MD5的值得到的,具体的描述如下:

  part-etag.PNG
 

实际测试了下,推测其具体的计算流程如下: 

  1. 将各个段的ETag按字符串拼接到一起

  2. 将拼接后的字符串从16进制形式转换成二进制数组(每个16进制的字符转换成4位二进制)

  3. 对转换后的二进制数组计算MD5值,然后再将计算结果的二进制数组转成16进制字符串,最后再加上“-总的段数量”

    Java示例代码大致如下:

           StringBuilder builder = new StringBuilder();

           for (String partETag : partETagList)

           {

               builder.append(partETag);

           }

           MessageDigest digest = MessageDigest.getInstance("MD5");

           byte[] digestValue = digest.digest(hexStringToBinary(builder.toString()));

           return binaryToHexString(digestValue) + "-" + totalPartNum;

    Java里16进制和二进制相互转换的方法,可以参考以下链接:

    https://blog.csdn.net/morgerton/article/details/78228884