13

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

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

4

8 回答 8

18

马特的回答完全正确。在这里它更新为新的 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。进步!

于 2013-12-09T16:44:59.963 回答
8

请参阅@bronson 的答案以获取使用较新的 RubyZip API 的此答案的更新版本。

您链接到的 Rubyzip 文档看起来有点旧。最新版本 (0.9.9) 可以处理IOobjects,因此您可以使用 StringIO(稍作调整)。

即使 api 将接受一个IO,它似乎仍然假设它是一个文件并尝试调用它,所以首先要添加一个方法的path猴子补丁(它实际上不需要做任何事情):StringIOpath

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

一切都将在内存中完成。

于 2012-12-05T21:23:31.027 回答
6
Zip::File.open_buffer(content) do |zip|
  zip.each do |entry|
    decompressed_data += entry.get_input_stream.read
  end
end
于 2015-07-31T17:43:51.830 回答
4

使用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
于 2017-04-09T05:00:04.350 回答
1

您可以使用Tempfile将 zip 文件转储到临时文件中。Tempfile 创建一个特定于操作系统的临时文件,在您的程序完成后将由操作系统清理。

于 2012-12-05T19:49:36.270 回答
1

受马特回答的启发,我为那些必须使用 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
}
于 2015-11-19T17:04:49.303 回答
1

这对我有用。就我而言,我只有一个文件,因此我使用了固定路径,但您可以使用它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
于 2017-05-02T23:23:04.557 回答
0

由于 ruby​​zip 的更改,仅对此进行了更新:

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
于 2014-05-27T00:27:05.640 回答