百度⽹盘如何⼤⽂件实现秒传?
百度⽹盘上传时,如果是超过256KB的⽂件,将计算整个⽂件的MD5和⽂件前256KB内容的MD5,并对两个MD5值加密后请求后端执⾏秒传。后端通过两个MD5和长度信息判断是否存在该⽂件,如果存在则完成秒传。
有个读者在上问我:百度⽹盘的秒传功能是如何实现的?大文件发送
这个问题我其实有想过,我猜测⼤概是前端计算⼀个⽂件的哈希值(⽐如MD5)发送给后端,⽹盘服务器判断是否存在这个⽂件,如果存在就直接在后端完成⽂件的“转存”,直接告诉前端:上传成功。
不过这是我⾃⼰猜测的,到底对不对,⼀直也没有去验证过。
我把我的猜测告诉了他,结果他问了⼀句:如果发⽣哈希冲突了怎么办呢?。
我想了⼀下⼜说:那就多加⼏个哈希!
不过百度⽹盘到底是怎么做的呢?这位读者既然问到了,我就趁机花了⼏分钟研究了⼀下,算是解答了这个疑惑,增加了知识。
MD5 冲突
⾸先,只⽤⼀个哈希值,已经有事实证明是会发⽣冲突的,⽽不只是理论上。
⽐如我在知乎上到了⼀个例⼦,下⾯两段不同的数据,只相差两个字节:
分别计算md5,结果是⼀样的:
所以,如果只⽤⼀个哈希值就判定是同⼀个⽂件,那就⽐较容易会出现张冠李戴的情况。
甚⾄,有⼈还基于此提出⼀种哈希碰撞攻击:如果我知道⼀个⽂件的md5值,但拿不到这个⽂件,我通过数学计算,构造⼀个相同md5的⽂件,那岂不是就把那个⽂件直接给我转存过来了?如果是⼀个私密的⽂件呢?那不出事了!
百度⽹盘的做法
那百度⽹盘是咋做的呢?
⾸先上传⼀个稍微⼤⼀点的⽂件(⼩⽂件有计算哈希的功夫早就传完了),使⽤浏览器F12⼤法,看⼀下它的⽹络请求:
可以看到,百度⽹盘对⽂件进⾏了分块传输,这也是⽬前业界⽐较流⾏的做法,对⼤⽂件进⾏分块,如果⽹络不好断开了,下次只需要传输剩下的分块就⾏了,做到了断点续传。
不过注意看,在上⾯分块的中间,插⼊了⼀个叫rapidupload接⼝的请求,从名字你也可以猜出来了,这个接⼝肯定跟它的“秒传”功能有关系
来看⼀下请求的参数,是⼀个Form表单,有这么⼏个字段:
content-length: ⽂件长度
content-md5: ⽂件的MD5
slice-md5: ⽂件切⽚的MD5
看到这⾥你估计猜到了,肯定是这三个参数联合判断,同时满⾜条件才算是同⼀个⽂件!
来看下服务器响应了什么:
秒传成功了!
那如果上传⼀个后端肯定不存在的⽂件会是返回什么呢?我构造了⼀个做测试:
看到了吧,404!说明后端没这个⽂件,那就⽼⽼实实传吧!
接着,我想看⼀下这个切⽚md5,百度⽹盘是怎么在切的。
通过⽹络通信中的Initiator功能,可以定位到是哪⾥的JS代码在发⽣请求:
通过调⽤堆栈,看到了叫rapidUpload这个函数,再上下⼀跟进,到了这个切⽚MD5计算的地⽅:
其实就是对⽂件的前262144个字节,也就是256KB进⾏计算。如果⽂件⽐这还⼩,那就⽤不着秒传了。
但奇怪的是,我扣取了⽂件的前256个字节,计算出来的md5,和它接⼝中上传的参数并不⼀致!
这让我疑惑了好⼏分钟,难道事情没这么简单?
我⼜打了断点在计算的位置,发现它计算的跟我计算的⼜是⼀样的,但通过⽹络发出去以后就变了,真是薛定谔的MD5,奇怪了!
不过,程序不是量⼦⼒学,它不会骗⼈,很快我就到了问题所在:百度⽹盘可能担⼼⾃⼰的路数被发现,对⽂件的MD5和切⽚MD5都进⾏了加密!
这就是加密函数:
⼀些简单的字符串处理⽽已。
好了,现在可以回答前⾯读者的问题了:
百度⽹盘上传时,如果是超过256KB的⽂件,将计算整个⽂件的MD5和⽂件前256KB内容的MD5,并对两个MD5值加密后请求后端执⾏秒传。后端通过两个MD5和长度信息判断是否存在该⽂件,如果存在则完成秒传。
这样做,虽然理论上也不能保证不会发⽣哈希碰撞,但通过这种⽅式,⾄少将概率降低了许多。
最后给⼤家留⼀个思考题:后端拿到MD5后,怎么判断后端是否有这个MD5呢? 这可是⼤⼚经常爱考的⼀个⾯试题哦,来开动脑筋想⼀下!
发布评论