【发布时间】:2010-09-10 02:06:28
【问题描述】:
在读取网页正文时,似乎 Ruby 的 Net::HTTP 方法要么全有,要么全无。例如,我如何读取正文的前 100 个字节?
我正在尝试从内容服务器读取,如果请求的文件不可用,该内容服务器会在响应正文中返回一条简短的错误消息。我需要阅读足够多的正文以确定文件是否存在。文件很大,所以我不想为了检查文件是否可用而获取整个文件。
【问题讨论】:
在读取网页正文时,似乎 Ruby 的 Net::HTTP 方法要么全有,要么全无。例如,我如何读取正文的前 100 个字节?
我正在尝试从内容服务器读取,如果请求的文件不可用,该内容服务器会在响应正文中返回一条简短的错误消息。我需要阅读足够多的正文以确定文件是否存在。文件很大,所以我不想为了检查文件是否可用而获取整个文件。
【问题讨论】:
这是一个旧线程,但根据我的研究,如何在 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
【讨论】:
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 个八位字节的行(如果服务器发送较小的块,则为多个较小的行)。
您不应该只使用 HTTP HEAD 请求(Ruby Net::HTTP::Head 方法)来查看资源是否存在,并且只有在收到 2xx 或 3xx 响应时才继续?这假定您的服务器配置为在文档不可用时返回 4xx 错误代码。我认为这是正确的解决方案。
另一种方法是请求 HTTP 标头并查看结果中的 content-length 标头值:如果您的服务器配置正确,您应该能够轻松分辨短消息和长文档之间的长度差异.另一种选择:在请求中设置content-range 标头字段(再次假定服务器的行为正确 WRT HTTP 规范)。
我不认为在您发送 GET 请求之后在客户端解决问题是可行的方法:到那时,网络已经完成了繁重的工作,而您不会真正节省任何浪费的资源。
【讨论】:
HEAD 是从客户端出发的正确方法。如果他们的服务器坏了,他们需要修复它。不幸的是,这并没有让 OP 的任务变得更容易,因为公司和供应商通常不关心使用内容的人在入侵他们的服务器时会遇到什么。
我想这样做一次,我唯一能想到的就是猴子修补Net::HTTP#read_body 和Net::HTTP#read_body_0 方法以接受长度参数,然后在前者中只需将长度参数传递给@ 987654323@ 方法,在这里您只能读取长度字节。
【讨论】:
要分块读取 HTTP 请求的正文,您需要像这样使用Net::HTTPResponse#read_body:
http.request_get('/large_resource') do |response|
response.read_body do |segment|
print segment
end
end
【讨论】:
Transfer-Encoding: chunked),如果我还在两个块中添加break(在两个ends 之前)以在获得第一个块后停止。在这种情况下,使用带有read_body 的块会使Ruby 不读取完整的响应(甚至不等待它)。但是,再说一遍:我的回答一开始就是分块的,而且这些都是小块。我怀疑 HTTP 是否允许客户端显式请求分块响应,也不允许它建议最大块大小;如果服务器不返回(小)块,似乎应该使用 Range 标头。
您确定内容服务器只返回一个简短的错误页面吗?
它是否还将HTTPResponse 设置为适当的值,例如404。在这种情况下,您可以捕获HTTPClientError 派生异常(很可能是HTTPNotFound),该异常在访问Net::HTTP.value() 时引发。
如果您收到错误,那么您的文件不存在 如果您收到 200 文件正在开始下载,您可以关闭连接。
【讨论】:
你不能。但你为什么需要?当然,如果页面只是说文件不可用,那么它就不会是一个大页面(即根据定义,文件不会在那里)?
【讨论】: