使用AliyunCLI进行OSS操作时,相关Content-MD5的说明

最近Support过程中遇到用户使用AliyunCLI这个集大成的工具进行OSS的相关操作,虽然OSS团队强烈建议用户使用OSS内部工具(如osscmd),但是对于购买并使用多种云产品的用户来说,节约学习和维护工具的成本是必要的,使用AliyunCLI来的更为舒服,当然,我们也愿意为亲爱的客户解决一切技术问题。

在介绍用户的使用场景之前,贴个AliyunCLI操作OSS的链接,给大家参考一下。

首先,用户的使用场景是使用AliyunCLI来完成本地文件的上传,在上传的过程中带上Content-MD5,进行文件的完整性校验。从上面的链接可以看出,可以使用如下的命令完成这个需求:
使用AliyunCLI进行OSS操作时,相关Content-MD5的说明
(细心的朋友应该发现,AliyunCLI的Put命令介绍是有问题的,有没有看出来?对,Put后面要跟个"localfile" )
使用AliyunCLI进行OSS操作时,相关Content-MD5的说明

用户使用的命令如下:
aliyuncli oss Put localfile oss://user_bucket_name --header "Content-MD5:userfile_md5" 


结果,不管“userfile_md5”是什么,这个localfile都能上传成功。是不是OSS的md5校验功能不work呢?当然不是哈,细心的朋友应该又发现了,是"--header"这个标签错误了,应该是"--headers",aliyuncli对于错误的标签是不提示错误直接忽略的,所以实际上这里上传的MD5值并没有写入oss请求的头部传入OSS。请千万注意哦~~


接下来,关于Content-MD5值如何生成,我们来详细介绍一下:

关于Content-MD5是啥,OSS官方文档介绍如下。因为HTTP的首部无法记录二进制值,所以需要通过base64编码转化为字符串。在OSS服务端,会使用相同的方法对报文主体进行计算得到对应的值并与报文首部的Content-MD5进行比较,来校验数据的有效性。

使用AliyunCLI进行OSS操作时,相关Content-MD5的说明
 从字面的解释很容易理解,md5值的获取就是两步:
   使用AliyunCLI进行OSS操作时,相关Content-MD5的说明
 所以,很容易的使用了最方便、现成的shell命令去做这件事了:
使用AliyunCLI进行OSS操作时,相关Content-MD5的说明

知道上面使用md5和base64命令生成的Content-MD5有什么不对么?

MD5 作为校验码,是一个 128 位长的二进制数。 在内存中,128 bits = 16 octets。 经过Base64 编码,长度增加约 33%,编码后的长度应为 4*⌈16/3⌉ = 24 字节。而上图中得到的结果却是44字节?!

这里的确有个坑!!!

我们先来研究一下算法的细节:
HTTP/1.1(RFC2616#14.15)给出了实体首部字段 Content-MD5 的语法规则:

Content-MD5   = "Content-MD5" ":" md5-digest
md5-digest   = <base64 of 128 bit MD5 digest as per RFC 1864>

即校验值的编码依据为 RFC1864:MD5 算法输出的结果为 128 位长的摘要。当以网络字节序(大端序) 解析时,可得到16字节的二进制数据序列。随后,将这 16 个字节按 base64 算法编码,最终得到可作为 `Content-MD5` 字段取值的结果。

在上面的shell命令例子中,我们首先使用md5命令对文件pom.xml的内容进行了MD5算法,得到一个128bit的二进制数,可以表示为0xOGI4NWYzYWZkNWY2OTRmMzQzMmM5YzQ5YWM1N2Q3ZGYK。然后使用base64进行了编码得出最后结果,这里到底哪儿有坑呢?
坑在于:错误的base64进行编码的不是对0xOGI4NWYzYWZkNWY2OTRmMzQzMmM5YzQ5YWM1N2Q3ZGYK这个数值,而是对"OGI4NWYzYWZkNWY2OTRmMzQzMmM5YzQ5YWM1N2Q3ZGYK"这串字符串!!

      这里,强调一下,是对128bit进行编码哦!附一段计算文件的Content-MD5的脚本给大家参考一下:

#-*-coding:utf-8-*-
#!/bin/env python

import md5
import sys
import base64

def md5file(fobj):
    m = md5.new()
    while True:
        d = fobj.read(8096)
        if not d:
            break
        m.update(d)
    return (str)(base64.b64encode(m.digest()))


if __name__ == '__main__':
    fname = sys.argv[1]
    f = file(fname, 'rb')
    print '%s' % (md5file(f))
    f.close()

 







---------------------------------------分割线---------------------------------------------------

诚聘英才


阿里云函数服务是一个全新的,支持事件驱动编程模式的计算服务。 他帮助用户聚焦自身业务逻辑,以Serverless的方式构建应用,快速的实现低成本,可扩展,高可用的系统,而无需考虑服务器等底层基础设施的管理。 用户能够快速的创建原型,同样的架构能随业务规模平滑伸缩。让计算变得更高效,更经济,更弹性,更可靠。无论小型创业公司,还是大型企业,都受益其中。

我们的团队正在迅速扩张,求贤若渴。我们想寻找这样的队友:

  • 基本功扎实。既能阅读论文追踪业界趋势,又能快速编码解决实际问题。
  • 严谨的,系统化的思维能力。既能整体考虑业务机会,系统架构,运维成本等诸多因素,又能掌控设计/开发/测试/发布的完整流程,预判并控制风险。
  • 好奇心和使命感驱动。乐于探索未知领域,不仅是梦想家,也是践行者。
  • 坚韧、乐观、自信。能在压力和困难中看到机会,让工作充满乐趣!

如果您对云计算充满热情,想要构建一个有影响力计算平台和生态体系,请加入我们,和我们一起实现梦想! 

详见:http://www.atatech.org/articles/53851

将你的简历发送到shuting.yst@alibaba-inc.com,标题  应聘阿里云-姓名

如果你有自己的git地址或者个人博客,将会大大加分哦,一起在邮件中发给我吧~~~


上一篇:十个城市数据可视化实例


下一篇:Sql Server之旅——第八站 复合索引和include索引到底有多大区别?