【问题标题】:How do I read only x number of bytes of the body using Net::HTTP?如何使用 Net::HTTP 仅读取正文的 x 个字节数?
【发布时间】:2010-09-10 02:06:28
【问题描述】:

在读取网页正文时,似乎 Ruby 的 Net::HTTP 方法要么全有,要么全无。例如,我如何读取正文的前 100 个字节?

我正在尝试从内容服务器读取,如果请求的文件不可用,该内容服务器会在响应正文中返回一条简短的错误消息。我需要阅读足够多的正文以确定文件是否存在。文件很大,所以我不想为了检查文件是否可用而获取整个文件。

【问题讨论】:

    标签: ruby http


    【解决方案1】:

    这是一个旧线程,但根据我的研究,如何在 Ruby 中通过 HTTP 仅读取文件的一部分的问题仍然是一个几乎没有答案的问题。这是我通过猴子修补 Net::HTTP 提出的一个解决方案:

    require 'net/http'
    
    # provide access to the actual socket
    class Net::HTTPResponse
      attr_reader :socket
    end
    
    uri = URI("http://www.example.com/path/to/file")
    begin
      Net::HTTP.start(uri.host, uri.port) do |http|
        request = Net::HTTP::Get.new(uri.request_uri)
        # calling request with a block prevents body from being read
        http.request(request) do |response|
          # do whatever limited reading you want to do with the socket
          x = response.socket.read(100);
          # be sure to call finish before exiting the block
          http.finish
        end
      end
    rescue IOError
      # ignore
    end
    

    rescue 捕获了当你过早调用 HTTP.finish 时抛出的 IOError。

    仅供参考,HTTPResponse 对象中的套接字不是真正的 IO 对象(它是一个名为 BufferedIO 的内部类),但也很容易对其进行修补以模仿 @987654326 @你需要的方法。例如,我正在使用的另一个库 (exifr) 需要 readchar 方法,该方法很容易添加:

    class Net::BufferedIO
      def readchar
        read(1)[0].ord
      end
    end
    

    【讨论】:

    • 请注意,响应可能会被压缩,然后在上面的示例中会得到 100 个“二进制”八位字节。当需要文本时,使用request = Net::HTTP::Get.new(uri.request_uri, {'Accept-Encoding' => 'entity'}) 禁用压缩。请注意,如果响应被分块,one will first get a line with the chunk size,以十六进制表示。因此,response.socket.read(100) 将产生一个十六进制数 64 的行,以及另一个 100 个八位字节的行(如果服务器发送较小的块,则为多个较小的行)。
    【解决方案2】:

    您不应该只使用 HTTP HEAD 请求(Ruby Net::HTTP::Head 方法)来查看资源是否存在,并且只有在收到 2xx 或 3xx 响应时才继续?这假定您的服务器配置为在文档不可用时返回 4xx 错误代码。我认为这是正确的解决方案。

    另一种方法是请求 HTTP 标头并查看结果中的 content-length 标头值:如果您的服务器配置正确,您应该能够轻松分辨短消息和长文档之间的长度差异.另一种选择:在请求中设置content-range 标头字段(再次假定服务器的行为正确 WRT HTTP 规范)。

    我不认为在您发送 GET 请求之后在客户端解决问题是可行的方法:到那时,网络已经完成了繁重的工作,而您不会真正节省任何浪费的资源。

    参考:http header definitions

    【讨论】:

    • 试过了,服务器发送一个OK响应和一个0作为内容长度。这是来自 Perforce 的 P4Web 服务器。
    • 嗯。如果您的供应商发送 200 OK 而实际上是 404 未找到,那么您应该向他们提出优先级错误报告!
    • 使用HEAD 是从客户端出发的正确方法。如果他们的服务器坏了,他们需要修复它。不幸的是,这并没有让 OP 的任务变得更容易,因为公司和供应商通常不关心使用内容的人在入侵他们的服务器时会遇到什么。
    【解决方案3】:

    我想这样做一次,我唯一能想到的就是猴子修补Net::HTTP#read_bodyNet::HTTP#read_body_0 方法以接受长度参数,然后在前者中只需将长度参数传递给@ 987654323@ 方法,在这里您只能读取长度字节。

    【讨论】:

    • 如果你碰巧还有代码,我很乐意看到它。
    • 很遗憾我手边没有,但它很简单,因为我只需要读取这些字节,而我并不关心后面的字节。所以我向#read_body 添加了另一个参数,默认为'nil',在#read_body_0 中我添加了参数len=nil,我有一些类似的东西: if len ; @socket.read 长度,目标;返回;结束
    【解决方案4】:

    要分块读取 HTTP 请求的正文,您需要像这样使用Net::HTTPResponse#read_body

    http.request_get('/large_resource') do |response|
      response.read_body do |segment|
        print segment
      end
    end
    

    【讨论】:

    • 试过这个。 request_get 仍然想在处理块之前下载整个文件。
    • 这适用于我的分块响应(使用Transfer-Encoding: chunked),如果我还在两个块中添加break(在两个ends 之前)以在获得第一个块后停止。在这种情况下,使用带有read_body 的块会使Ruby 不读取完整的响应(甚至不等待它)。但是,再说一遍:我的回答一开始就是分块的,而且这些都是小块。我怀疑 HTTP 是否允许客户端显式请求分块响应,也不允许它建议最大块大小;如果服务器不返回(小)块,似乎应该使用 Range 标头。
    【解决方案5】:

    您确定内容服务器只返回一个简短的错误页面吗?

    它是否还将HTTPResponse 设置为适当的值,例如404。在这种情况下,您可以捕获HTTPClientError 派生异常(很可能是HTTPNotFound),该异常在访问Net::HTTP.value() 时引发。

    如果您收到错误,那么您的文件不存在 如果您收到 200 文件正在开始下载,您可以关闭连接。

    【讨论】:

      【解决方案6】:

      你不能。但你为什么需要?当然,如果页面只是说文件不可用,那么它就不会是一个大页面(即根据定义,文件不会在那里)?

      【讨论】:

      • 这不是答案。这是您无法想象某些东西是必要的/有用的/可取的,因为您个人还没有遇到它。谁在乎他为什么需要?谁在乎你是否在这个问题上有一些结局?问题是“如何使用 Net::HTTP 仅读取正文的 x 个字节?”。你知不知道怎么?如果不是,为什么要浪费大家的带宽?
      猜你喜欢
      • 2014-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-22
      相关资源
      最近更新 更多