在Ruby中使用Net :: HTTP比较本地文件与校验和,而不下载完整文件

时间:2013-04-29 19:47:03

标签: ruby http-headers checksum fingerprint net-http

如果无法控制或添加服务器端头,是否可以将本地校验和与远程文件进行比较,而无需下载整个文件并使用Ruby和Net :: HTTP比较校验和?

我正在使用我使用Net :: HTTP编写的类填充文件磁盘,并希望通过将远程文件与本地文件的SHA256总和进行比较来增加带宽节省;我只想在本地副本与远程版本不匹配时下载远程文件。

以下是我的假设:

  • 文件名可能相同,但内容可能不同。

  • HTTP标头中的“上次修改”日期并不是更改的良好指示 - cp /dir_a/file1.tar /dir_b/file2.tar会产生相同的校验和,但会有“最后修改”次数。

  • HTTP标头Etags不是一个好的指标:http://example.org/file1.tarhttp://example.iana.org//file1.tar可能对同一个文件有不同的Etags。

  • HTTP标头Etags不是完全标准的 - 而EC2使用md5sums生成Etags,其他主机可能没有。这使得本地生成此标记值变得困难。

  • 维护hostname-to-Etag实现的哈希/字典是不实用的,也是一种糟糕的方法。

虽然我相对确定服务器端软件必须提供一个工具来进行文件/标签/校验和比较才能实现这一目标(例如标题中的校验和字段或单独的查找文件),在放弃这种追求之前,我想确认我的假设。我已经省略了我现有的代码以避免分心,因为我正在寻求如何实现。

1 个答案:

答案 0 :(得分:1)

不幸的是,对于我的用例,没有办法使用标准HTTP头或Net :: HTTP请求获得预先计算的校验和。

<强>解决方案

如果您控制服务器,则可以添加任意标头,例如NginxApache

或者,可以创建并公开带有文件/校验和的键/值对的结构化字典文件,例如JSON中的以下(粗略)示例:

{ "md5-files": [
    {"file1" : "60b725f10c9c85c70d97880dfe8191b3"}, 
    {"file2" : "18ac6fe7ca693bb1767982e2eb3bbd0d")
]}

如果要在多台服务器上镜像相同的文件,那么可能值得构建这样的结构化阵列本地并且仅使用一台服务器来表示文件已更改远程(例如master-download-server-1从http://example.org/file1下载文件,将其与本地版本进行比较,然后更新文件。该文件可以由slave-download-server1解析,slave- download-server2确定他们是否应该向example.org(或master-download-server-1本身)发送请求。

最后,当我经常从亚马逊的S3下载时,我只使用了可以作为仅客户端服务使用的选项:依赖于标题中返回的 etag 。不幸的是,这方面的文档不是很好,但这是我的方法的粗略片段:

...
#I actually call my own encryption-helper, filename-parsing methods, 
#but meta-code for the sake of example:
def example_file_getter(uri, docroot, file)
    checksum = Digest::MD5.hexdigest(File.read(file))

    uri = URI.parse(uri)
    http = Net::HTTP.new(uri.host, uri.port)
    request = Net::HTTP::Get.new(uri.request_uri)
    response = http.request(request)

    if response['etag'] != nil
        etag = response['etag'].gsub!(/\"/,'')
    end

    if etag == checksum
      file_existed = true
    end

    if ! File::exists?(destination) && ! file_existed
    ...actually fetch the file    
...

[再次,元代码;这是与我原来的问题相关的重要部分的摘要]

同样,etag文档并不是很好,我完全希望亚马逊在某些方面没有任何警告就改变它。从我从亚马逊员工的各种forum responses(!!)中拼凑而成的标签的一般算法如下:

  1. 如果文件小于5GB且已上传/非“多个”上传到服务器,则etag可能是上传文件的md5。
  2. 如果文件大于5GB或通过'multipart'上传,etag似乎是上传文件的最后一块,用md5-#表示,其中#是文件的一部分(例如3个上传文件块会看起来像标题中的18ac6fe7ca693bb1767982e2eb3bbd0d-3。
  3. 不完美,但如果您的远程主机遵循可预测的模式,请检查标题并希望获得最佳效果。