我正在编写一个单元测试,其中一个正在返回一个 zip 文件,我想检查这个 zip 文件的内容,从中获取一些值,并将这些值传递给下一个测试。
我正在使用 Rack Test,所以我知道我的 zip 文件的内容在last_response.body
. 我查看了RubyZip的文档,但似乎它总是需要一个文件。由于我正在运行单元测试,因此如果可能的话,我更喜欢在内存中完成所有操作,以免使用测试 zip 文件污染任何文件夹。
马特的回答完全正确。在这里它更新为新的 API:
Zip::InputStream.open(StringIO.new(input)) do |io|
while entry = io.get_next_entry
if entry.name == 'doc.kml'
parse_kml(io.read)
else
raise "unknown entry in kmz file: #{entry.name}"
end
end
end
并且不再需要对 StringIO 进行monkeypatch。进步!
请参阅@bronson 的答案以获取使用较新的 RubyZip API 的此答案的更新版本。
您链接到的 Rubyzip 文档看起来有点旧。最新版本 (0.9.9) 可以处理IO
objects,因此您可以使用 StringIO(稍作调整)。
即使 api 将接受一个IO
,它似乎仍然假设它是一个文件并尝试调用它,所以首先要添加一个方法的path
猴子补丁(它实际上不需要做任何事情):StringIO
path
require 'stringio'
class StringIO
def path
end
end
然后您可以执行以下操作:
require 'zip/zip'
Zip::ZipInputStream.open_buffer(StringIO.new(last_response.body)) do |io|
while (entry = io.get_next_entry)
# deal with your zip contents here, e.g.
puts "Contents of #{entry.name}: '#{io.read}'"
end
end
一切都将在内存中完成。
Zip::File.open_buffer(content) do |zip|
zip.each do |entry|
decompressed_data += entry.get_input_stream.read
end
end
使用RubyZip版本1.2.1
(或者可能还有一些以前的版本),我们只需要使用类open_buffer
的方法Zip::File
。
来自 RubyZip 文档:
与#open 类似,但从字符串或打开的 IO 流中读取 zip 存档内容,并将数据输出到缓冲区。(这可用于从下载的 zip 存档中提取数据,而无需先将其保存到磁盘。)
例子:
Zip::File.open_buffer(last_response.body) do |zip|
zip.each do |entry|
puts entry.name
# Do whatever you want with the content files.
end
end
您可以使用Tempfile
将 zip 文件转储到临时文件中。Tempfile 创建一个特定于操作系统的临时文件,在您的程序完成后将由操作系统清理。
受马特回答的启发,我为那些必须使用 0.9.x rubyzip gem 的人提供了一个稍微修改的解决方案。我的不需要新的类定义。
sio = StringIO.new(response.body)
sio.define_singleton_method(:path) {} #needed to create fake method path TO satisfy the ancient rubyzip 0.9.8 gem
Zip::ZipInputStream::open_buffer(sio) { |io|
while (entry = io.get_next_entry)
puts "Contents of #{entry.name}"
end
}
这对我有用。就我而言,我只有一个文件,因此我使用了固定路径,但您可以使用它entry.name
来构建路径。
input = HTTParty.get(link).body
Zip::File.open_buffer(input) do |zip_file|
zip_file.each do |entry|
entry.extract(path)
end
end
由于 rubyzip 的更改,仅对此进行了更新:
Zip::InputStream.open(StringIO.new(zip_file)) do |io|
while (entry = io.get_next_entry)
# deal with your zip contents here, e.g.
puts "Contents of #{entry.name}: '#{io.read}'"
end
end