rubyzip
| Language | Ruby (3.0+ for rubyzip 3.x) |
|---|---|
| License | BSD 2-clause |
| Repository | github.com/rubyzip/rubyzip |
| Documentation | README, API docs |
| Depends on | Ruby's bundled zlib |
| Read / write | yes / yes, ZIP64, stored and DEFLATE, AES (read 3.1+, write 3.7+) and ZipCrypto |
rubyzip has two APIs:
Zip::File: random access to an archive. Read entries, add, rename and remove files; changes are written oncommitor at the end of theopenblock.Zip::OutputStream/Zip::InputStream: forward-only streams. Use them to generate a ZIP in memory (for example to send from a Rails controller), or to use encryption, which is only available with the stream API.
Version 3.0 changed optional arguments to keyword arguments (create: true, destination_directory:), so code written for 2.x needs small changes.
Installation#
Shell
gem install rubyzip
Or in a Gemfile:
Ruby
gem "rubyzip", "~> 3.0"
Basic usage: extract a ZIP file#
Ruby
require "zip"
MAX_SIZE = 100 * 1024 * 1024 # refuse entries larger than 100 MB
Zip::File.open("in.zip") do |zip|
zip.each do |entry|
puts "#{entry.name} #{entry.size} bytes"
raise "#{entry.name} too large" if entry.size > MAX_SIZE
end
# writes every entry under out/, creating folders;
# names that would land outside out/ are skipped with a warning
zip.extract_all("out")
end
To extract a single entry use entry.extract(destination_directory: "out", create_parent_directories: true); without that option, current versions don’t create missing parent folders.
How to create a ZIP file#
Ruby
require "zip"
Zip::File.open("out.zip", create: true) do |zip|
zip.add("docs/report.pdf", "report.pdf") # name in archive, file on disk
zip.get_output_stream("hello.txt") { |f| f.write "hello from Ruby\n" }
end
How to read one file from a ZIP#
Ruby
require "zip"
Zip::File.open("in.zip") do |zip|
entry = zip.find_entry("docs/readme.txt")
puts entry.get_input_stream.read if entry
# or by pattern
zip.glob("*.csv").each { |e| puts e.name }
end
How to create a ZIP in memory#
Ruby
require "zip"
buffer = Zip::OutputStream.write_buffer do |out|
out.put_next_entry("report.csv")
out.write "id,name\n1,Alice\n"
end
data = buffer.string # the ZIP bytes, e.g. for send_data in Rails
How to create and read an AES-encrypted ZIP#
Ruby
require "zip"
enc = Zip::AESEncrypter.new("my password", Zip::AESEncryption::STRENGTH_256_BIT)
Zip::OutputStream.open("secret.zip", encrypter: enc) do |out|
out.put_next_entry("secret.txt")
out.write "top secret"
end
dec = Zip::AESDecrypter.new("my password", Zip::AESEncryption::STRENGTH_256_BIT)
Zip::InputStream.open("secret.zip", decrypter: dec) do |input|
while (entry = input.get_next_entry)
puts "#{entry.name}: #{input.read}"
end
end
For ZipCrypto use Zip::TraditionalEncrypter / Zip::TraditionalDecrypter with just the password.
Tips#
entry.sizecomes from the archive header and can lie; when extracting untrusted archives, also count the bytes you actually read.Zip.default_compression = Zlib::BEST_COMPRESSIONsets the level for new entries.Zip::InputStreamcan’t read entries whose sizes are only in a data descriptor; it raisesZip::StreamingError. UseZip::Filefor those.