想起以前做项目,用到了Rsync check 文件内容,未免以后忘记,现在整理下 大致逻辑
背景:
我们新建一个文件,上传,再改动一点点东西,通用办法就是,把改动后的文件,上传覆盖以前的文件,这样不会错,但是有个问题,如果这个文件很大,那么整个上传就会消耗大量的时间和流量,哪怕我们只是编辑了一个标点符号。
解决方案:
那么有没有什么办法,我只上传编辑的部分,那就万事大吉了,但是没有编辑的部分,我怎么告诉server 呢,我可以告诉server ,我没有改变的部分的 offset ,比如: 0-121 byte,180- 300byte,编辑的部分我上传data,那么最后的结果是什么呢:
0-121byte(location),122-179byte(data),180-300 byte.
那么,我怎么找出编辑的部分呢,大致方案如下:
1. 我将原内容分割成若干部分(具体每部分多大,根据文件大小确定),每部分通过算法,得到两个值,sha -> sum1,md5 ->sum2
2. 我将编辑后的文件,按照同样大小,分割,依次对比,如果发现sum1 和 sum2 都一样,那么我们认为这部分是以前的内容,记录下来
3. 遇到sum1 不一致,或者sum1一致,sum2 不一致的内容(这就是为什么要通过两个算法去校验,sum2 准确,但是消耗性能,sum1 性能消耗较低,但是不准确),那么我们往后移动一个byte,这一个byte 作为新增的内容,记录下位置,接着从移动过后的位置对比
4. 一个字节一个字节的添加太麻烦,还有如果重用的部分是连续的,那么我们可以添加逻辑把连续新增的字节或者连续重用的字节连起来,最后上传的时候,再上传连起来的部分的offset 和data
大致的逻辑就是这样,但是有个问题:
原内容:[(sum1,sum2),(sum1,sum2),(sum1,sum2).......]
我们计算编辑后的文件的时候,拿到sum1 和sum2 ,怎么去对比呢,要知道,上面的数组是相当庞大的,你要挨个去对比,我的天,性能灾难,得不偿失。
有什么办法呢,如果能想key ,value 这样挨个对应起来就好了,这样,直接找key,看value 是否有值。
原内容:[{sum1:{sum2:xxxxx,location :123-456}}.......]
但是,这样有新的问题:
1. sum1 有可能碰撞(不精确)
2. sum1 还是很长,key value 都很长,这样的性能我们是无法接受的
如果有什么办法,能够将sum1 或者sum2 编程一个数字或者简单的字符就好了。
网上找找,果然有,将可以通过一个算法将sum1 变成一个 数字:
f(sum1) -> 10233
我们再构建一个hashtable 样子, 表的样子是什么呢
hashTable[10233] = i, i 表示在原文件中的第几端,至于其他的都设置成-1,最后结果
hashTable[0] = -1,....hashTable[10233] = 0,......
我们拿到编辑后的文件段的sum1, 通过同样的运算, 获得的值也是 10233, 那么我们拿这个10233 反过来找hashTable, 发现value 不是-1, 恭喜你可能找到了,这时再对比下sum2,这样就能最终确定了,这样是不是性能提高了很多
但是还有一个问题,不同内容的sum1 可能一样啊,这咋整,也就是,可能第57 段和第89段的sum1都是10233,那么结果就是,后面的把前面的覆盖,结果就是 hashTabel[10233] = 89
这咋整?
我们想如果能记录后一段,也就是在覆盖前一段的时候能够把前一段的location 记录下来就好了,ok,那么我们新定义一个变量chain,通常情况下,chain值为-1,只有发现hashTable[10233]有值时,我们把后一个位置的chain 设置成前一个位置的location,这样是不是就不怕覆盖了,结果就是,假设第0,57,89 的结果都是10233,那么:
hashTable[10233] = 89, data[89].chain = 57, data[57].chain = 0
代码如下:
for I in 0.. let sumData = file.sums[I] let t =self.getHashEntry( sum1: sumData.sum1,true) let subsum = file.sums[i] subsum.chain=hashTable[Int(t)] self.hashTable[Int(t)] = i } perfect!