如何在Ruby中迭代内存中的zip文件

我正在写一个unit testing,其中一个正在返回一个zip文件,我想检查这个zip文件的内容,从中获取一些值,并将值传递给下一个测试。

我正在使用Rack Test,所以我知道我的zip文件的内容在last_response.body 。 我查看了RubyZip的文档,但似乎总是期待一个文件。 由于我正在运行unit testing,我更喜欢在内存中完成所有操作,以免污染任何带有测试zip文件的文件夹,如果可能的话。

请使用更新的RubyZip API查看@bronson的答案 ,了解此答案的最新版本。

您链接的Rubyzip文档看起来有点旧。 最新版本(0.9.9)可以处理IO对象 ,因此您可以使用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, eg puts "Contents of #{entry.name}: '#{io.read}'" end end 

一切都将在记忆中完成。

马特的答案是完全正确的。 这里它更新为新的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 

并且不再需要monkeypatch StringIO。 进展!

 Zip::File.open_buffer(content) do |zip| zip.each do |entry| decompressed_data += entry.get_input_stream.read end end 

使用RubyZip 1.2.1版(或者也许是以前的版本),我们只需要使用Zip::File类的open_buffer方法。

来自RubyZip文档:

与#open一样,但是从String或打开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创建一个特定于操作系统的临时文件,程序完成后将由操作系统清理。

这对我有用。 在我的情况下,我只有一个文件,所以我使用固定路径,但您可以使用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, eg puts "Contents of #{entry.name}: '#{io.read}'" end end 

受Matt的回答启发,对于那些必须使用0.9.x ruby​​zip 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 }