How can I change a zip file only in memory?

I have a Ruby application and I need to modify an existing zip file.

I want to create a zip file in memory and transfer bytes back without writing the file to the file system. If I finish posting this on Heroku, I donโ€™t think I can write to the file system. Does anyone know how to do this?

I looked at Zip :: ZipFile , but it looks like it always wants to write to the file system. I realized that โ€œbased on the implementation of Javaโ€ I could just get the compressed bytes of the files you can do in java, but I see no way to do this.


Edit:

What I'm asking for is basically the same as this, but for Ruby instead of Python: Function to create a zip file in memory and return as an HTTP response

+6
ruby ruby-on-rails zip
source share
4 answers

had the same problem to make it work by closing the file and reading data and passing it as send_data p>

then I found another library that works fine on heroku and can process buffers in memory: zipruby (not rubyzip).

buffer = '' Zip::Archive.open_buffer(buffer, Zip::CREATE) do |archive| files.each do |wood, report| title = wood.abbreviation+".txt" archive.add_buffer(title, report); end end file_name = "dimter_#{@offer.customerName}_#{Time.now.strftime("%m%d%Y_%H%M")}.zip" send_data buffer, :type => 'application/zip', :disposition => 'attachment', :filename => file_name 
+4
source share

Here 's a blog post that addresses this issue. It uses Tempfile and seems like a great solution for me (although I read the comments for some useful additional discussion).

Example from the message:

 def download_zip(image_list) if !image_list.blank? file_name = "pictures.zip" t = Tempfile.new("my-temp-filename-#{Time.now}") Zip::ZipOutputStream.open(t.path) do |z| image_list.each do |img| title = img.title title += ".jpg" unless title.end_with?(".jpg") z.put_next_entry(title) z.print IO.read(img.path) end end send_file t.path, :type => 'application/zip', :disposition => 'attachment', :filename => file_name t.close end end 

This solution should go well with Heroku .

+3
source share

You can always fix the new and open Zip :: ZipFile methods to allow the use of StringIO handles, and then do your I / O directly into memory.

+1
source share

Intending to offer an answer to my own question here, I think it is better suited to what I was trying to do. This method does not really make a file (without a temporary file).

Since ZipFile extends and really represents a bunch of convenient methods in ZipCentralDirectory, you can work directly with ZipCentralDirectory instead of ZipFile. This will allow you to use I / O streams to create and write a zip file. Plus, using StringIO, and you can do this from a string:

  # load a zip file from a URL into a string resp = Net::HTTP.new("www.somewhere.com", 80).get("/some.zip") zip_as_string = response.body # open as a zip zip = Zip::ZipCentralDirectory.read_from_stream(StringIO.new(zip_as_string)) # work with the zip file. # i just output the names of each entry to show that it was read correctly zip.each { |zf| puts zf.name } # write zip back to an output stream out = StringIO.new zip.write_to_stream(out) # use 'out' or 'out.string' to do whatever with the resulting zip file. out.string 

Update:

It actually doesn't work at all. He will write a readable zip file, but ONLY the zip file "Table of Contents". All internal files are 0 in length. Digging further into the Zip implementation, it looks like it only stores metadata in a zip record in memory and returns to the main file to read the rest. Based on this, it seems that it is generally impossible to use the Zip implementation without writing to the file system.

+1
source share

All Articles