How can I generate zip file without saving to the disk with Ruby?
I had a similar problem which I solved using the rubyzip gem and the stringio object.
It turns out that rubyzip provides a method that returns a stringio object: ZipOutputStream.write_buffer
.
You can create the zip file structure as you like using put_next_entry
and write and once you are finished you can rewind the stringio and read the binary data using sysread.
See the following simple example (works for rubyzip 0.9.X)
require 'zip/zip'
stringio = Zip::OutputStream.write_buffer do |zio|
zio.put_next_entry("test.txt")
zio.write "Hello world!"
end
stringio.rewind
binary_data = stringio.sysread
Tested on jruby 1.6.5.1 (ruby-1.9.2-p136) (2011-12-27 1bf37c2) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_29) [Windows Server 2008-amd64-java])
The following example works for rubyzip >= 1.0.0
require 'rubygems'
require 'zip'
stringio = Zip::OutputStream.write_buffer do |zio|
zio.put_next_entry("test.txt")
zio.write "Hello world!"
end
binary_data = stringio.string
Tested on jruby 1.7.22 (1.9.3p551) 2015-08-20 c28f492 on OpenJDK 64-Bit Server VM 1.7.0_79-b14 +jit [linux-amd64] and rubyzip gem 1.1.7
How can I create a zip file without temporary files in ruby?
See the answer at "How can I generate zip file without saving to the disk with Ruby?"
I adapted your example to demonstrate it works.
require 'zip/zip'
zipname = 'test.zip'
File.delete(zipname) if File.exists?(zipname) #delete previous version
stringio = Zip::ZipOutputStream::write_buffer do |zio|
1.upto(5) do |i| #Just some testfiles with content
zio.put_next_entry("test#{i}.txt") #Filename
zio.write("Testcontent %08i" % i) #generated content
sleep 1 #sleep some time to see the temporary files
end
end
stringio.rewind #reposition buffer pointer to the beginning
File.new("test.zip","wb").write(stringio.sysread) #write buffer to zipfile
How to iterate through an in-memory zip file in Ruby
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.
Create in-memory only gzip
You're actually gzipping normal.to_s
(which is something like "#<File:0x007f53c9b55b48>"
) in the following code.
# Files
normal = File.new('chunk0.nbt')
# Try to create gzip in program
make_gzip normal
You should read the content of the file, and make_gzip
on the content:
make_gzip normal.read
As I commented, the make_gzip
can be updated:
def self.make_gzip(data)
gz = Zlib::GzipWriter.new(StringIO.new)
gz << data
gz.close.string
end
Processing Zip files in Ruby
Hi I'm using rubyzip library. You can do something like this:
Zip::ZipFile.foreach(file) do |entry|
istream = entry.get_input_stream
data = istream.read
#...
end
Related Topics
Ruby on Rails - Paperclip Not Saving to Database
How to Combination/Permutation in Ruby
How to Reinstall Ruby with Readline Support
Jekyll Plugin Not Work on Github
Error: Error Installing JSON: Error: Failed to Build Gem Native Extension
Ruby Range: Operators in Case Statement
Can Anyone Explain This Array Declaration to Me
Change Emacs Ruby-Mode Indent to 4 Spaces
How to Force To_Yaml to Output Long Strings in Literal Block Style
Ruby Block Taking Array or Multiple Parameters
Nameerror: Undefined - Have Parsing Rules for Local Variables Changed in Ruby 2.1.2
Bundle Install Is Using a Different Ruby Version
Config Undefined in Environment Specific Configuration Files