Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to iterate through an in-memory zip file in Ruby

I am writing a unit test, and one of them is returning a zip file and I want to check the content of this zip file, grab some values from it, and pass the values to the next tests.

I'm using Rack Test, so I know the content of my zip file is inside last_response.body. I have looked through the documentation of RubyZip but it seems that it's always expecting a file. Since I'm running a unit test, I prefer to have everything done in the memory as not to pollute any folder with test zip files, if possible.

like image 935
garbagecollector Avatar asked Dec 05 '12 19:12

garbagecollector


2 Answers

See @bronson’s answer for a more up to date version of this answer using the newer RubyZip API.

The Rubyzip docs you linked to look a bit old. The latest release (0.9.9) can handle IO objects, so you can use a StringIO (with a little tweaking).

Even though the api will accept an IO, it still seems to assumes it’s a file and tries to call path on it, so first monkey patch StringIO to add a path method (it doesn’t need to actually do anything):

require 'stringio'
class StringIO
  def path
  end
end

Then you can do something like:

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

and everything will be done in memory.

like image 117
matt Avatar answered Sep 28 '22 07:09

matt


With RubyZip version 1.2.1 (or maybe some previous versions too), we just need to use open_buffer method of Zip::File class.

From RubyZip documentation:

Like #open, but reads zip archive contents from a String or open IO stream, and outputs data to a buffer. (This can be used to extract data from a downloaded zip archive without first saving it to disk.)

Example:

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
like image 41
Prodis Avatar answered Sep 28 '22 06:09

Prodis