2

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

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

以下是我的假设:

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

  • HTTP 标头中的“Last-modified”日期并不能很好地指示更改 -cp /dir_a/file1.tar /dir_b/file2.tar导致校验和相同,但“Last-modified”时间不同。

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

  • HTTP 标头 Etag 并不完全是标准的——虽然 EC2 使用 md5sums 来生成它们的 Etag,但其他主机可能不会。这使得该标记值的本地生成变得困难。

  • 维护主机名到 Etag 实现的散列/字典是笨拙且不好的方法。

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

4

1 回答 1

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 解析以确定它们是否应该向示例发送请求。 org(或 master-download-server-1 本身)。

最后,当我经常从 Amazon 的 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 文档不是很好,我完全希望亚马逊在某些时候会在没有警告的情况下更改它。根据我从亚马逊员工的各种论坛回复(!!)中拼凑出来的内容,标签的一般算法如下:

  1. 如果文件小于 5GB 并且被流式传输/非“多部分”上传到服务器,则 etag 可能是上传文件的 md5。
  2. 如果文件大于 5GB 或通过 'multipart' 上传,etag 似乎是上传文件的最后一块,用 md5-# 表示,其中 # 是文件的一部分(例如,上传文件的 3 块看起来像 18ac6fe7ca693bb1767982e2eb3bbd0d- 3 在标题中)。

不完美,但如果您的远程主机遵循可预测的模式,请检查标头并希望最好。

于 2013-04-30T15:32:46.390 回答