如何在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 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 }