Git Product home page Git Product logo

rubyzip's Introduction

rubyzip

Gem Version Tests Linter Code Climate Coverage Status

Rubyzip is a ruby library for reading and writing zip files.

Important notes

Updating to version 3.0

The public API of some classes has been modernized to use named parameters for optional arguments. Please check your usage of the following Rubyzip classes:

  • File
  • Entry
  • InputStream
  • OutputStream

Please see Updating to version 3.x in the wiki for details.

Requirements

Version 3.x requires at least Ruby 3.0.

Version 2.x requires at least Ruby 2.4, and is known to work on Ruby 3.1.

It is not recommended to use any versions of Rubyzip earlier than 2.3 due to security issues.

Installation

Rubyzip is available on RubyGems:

gem install rubyzip

Or in your Gemfile:

gem 'rubyzip'

Usage

Basic zip archive creation

require 'rubygems'
require 'zip'

folder = "Users/me/Desktop/stuff_to_zip"
input_filenames = ['image.jpg', 'description.txt', 'stats.csv']

zipfile_name = "/Users/me/Desktop/archive.zip"

Zip::File.open(zipfile_name, create: true) do |zipfile|
  input_filenames.each do |filename|
    # Two arguments:
    # - The name of the file as it will appear in the archive
    # - The original file, including the path to find it
    zipfile.add(filename, File.join(folder, filename))
  end
  zipfile.get_output_stream("myFile") { |f| f.write "myFile contains just this" }
end

Zipping a directory recursively

Copy from here

require 'zip'

# This is a simple example which uses rubyzip to
# recursively generate a zip file from the contents of
# a specified directory. The directory itself is not
# included in the archive, rather just its contents.
#
# Usage:
#   directory_to_zip = "/tmp/input"
#   output_file = "/tmp/out.zip"
#   zf = ZipFileGenerator.new(directory_to_zip, output_file)
#   zf.write()
class ZipFileGenerator
  # Initialize with the directory to zip and the location of the output archive.
  def initialize(input_dir, output_file)
    @input_dir = input_dir
    @output_file = output_file
  end

  # Zip the input directory.
  def write
    entries = Dir.entries(@input_dir) - %w[. ..]

    ::Zip::File.open(@output_file, create: true) do |zipfile|
      write_entries entries, '', zipfile
    end
  end

  private

  # A helper method to make the recursion work.
  def write_entries(entries, path, zipfile)
    entries.each do |e|
      zipfile_path = path == '' ? e : File.join(path, e)
      disk_file_path = File.join(@input_dir, zipfile_path)

      if File.directory? disk_file_path
        recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
      else
        put_into_archive(disk_file_path, zipfile, zipfile_path)
      end
    end
  end

  def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
    zipfile.mkdir zipfile_path
    subdir = Dir.entries(disk_file_path) - %w[. ..]
    write_entries subdir, zipfile_path, zipfile
  end

  def put_into_archive(disk_file_path, zipfile, zipfile_path)
    zipfile.add(zipfile_path, disk_file_path)
  end
end

Save zip archive entries sorted by name

To save zip archives with their entries sorted by name (see below), set ::Zip.sort_entries to true

Vegetable/
Vegetable/bean
Vegetable/carrot
Vegetable/celery
fruit/
fruit/apple
fruit/kiwi
fruit/mango
fruit/orange

Opening an existing zip file with this option set will not change the order of the entries automatically. Altering the zip file - adding an entry, renaming an entry, adding or changing the archive comment, etc - will cause the ordering to be applied when closing the file.

Default permissions of zip archives

On Posix file systems the default file permissions applied to a new archive are (0666 - umask), which mimics the behavior of standard tools such as touch.

On Windows the default file permissions are set to 0644 as suggested by the Ruby File documentation.

When modifying a zip archive the file permissions of the archive are preserved.

Reading a Zip file

MAX_SIZE = 1024**2 # 1MiB (but of course you can increase this)
Zip::File.open('foo.zip') do |zip_file|
  # Handle entries one by one
  zip_file.each do |entry|
    puts "Extracting #{entry.name}"
    raise 'File too large when extracted' if entry.size > MAX_SIZE

    # Extract to file or directory based on name in the archive
    entry.extract

    # Read into memory
    content = entry.get_input_stream.read
  end

  # Find specific entry
  entry = zip_file.glob('*.csv').first
  raise 'File too large when extracted' if entry.size > MAX_SIZE
  puts entry.get_input_stream.read
end

Notes on Zip::InputStream

Zip::InputStream can be used for faster reading of zip file content because it does not read the Central directory up front.

There is one exception where it can not work however, and this is if the file does not contain enough information in the local entry headers to extract an entry. This is indicated in an entry by the General Purpose Flag bit 3 being set.

If bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written. The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure (optionally preceded by a 4-byte signature) immediately after the compressed data.

If Zip::InputStream finds such an entry in the zip archive it will raise an exception (Zip::StreamingError).

Zip::InputStream is not designed to be used for random access in a zip file. When performing any operations on an entry that you are accessing via Zip::InputStream.get_next_entry then you should complete any such operations before the next call to get_next_entry.

zip_stream = Zip::InputStream.new(File.open('file.zip'))

while entry = zip_stream.get_next_entry
  # All required operations on `entry` go here.
end

Any attempt to move about in a zip file opened with Zip::InputStream could result in the incorrect entry being accessed and/or Zlib buffer errors. If you need random access in a zip file, use Zip::File.

Password Protection (Experimental)

Rubyzip supports reading/writing zip files with traditional zip encryption (a.k.a. "ZipCrypto"). AES encryption is not yet supported. It can be used with buffer streams, e.g.:

Version 2.x

# Writing.
enc = Zip::TraditionalEncrypter.new('password')
buffer = Zip::OutputStream.write_buffer(::StringIO.new(''), enc) do |output|
  output.put_next_entry("my_file.txt")
  output.write my_data
end

# Reading.
dec = Zip::TraditionalDecrypter.new('password')
Zip::InputStream.open(buffer, 0, dec) do |input|
  entry = input.get_next_entry
  puts "Contents of '#{entry.name}':"
  puts input.read
end

Version 3.x

# Writing.
enc = Zip::TraditionalEncrypter.new('password')
buffer = Zip::OutputStream.write_buffer(encrypter: enc) do |output|
  output.put_next_entry("my_file.txt")
  output.write my_data
end

# Reading.
dec = Zip::TraditionalDecrypter.new('password')
Zip::InputStream.open(buffer, decrypter: dec) do |input|
  entry = input.get_next_entry
  puts "Contents of '#{entry.name}':"
  puts input.read
end

This is an experimental feature and the interface for encryption may change in future versions.

Known issues

Modify docx file with rubyzip

Use write_buffer instead open. Thanks to @jondruse

buffer = Zip::OutputStream.write_buffer do |out|
  @zip_file.entries.each do |e|
    unless [DOCUMENT_FILE_PATH, RELS_FILE_PATH].include?(e.name)
      out.put_next_entry(e.name)
      out.write e.get_input_stream.read
    end
  end

  out.put_next_entry(DOCUMENT_FILE_PATH)
  out.write xml_doc.to_xml(:indent => 0).gsub("\n","")

  out.put_next_entry(RELS_FILE_PATH)
  out.write rels.to_xml(:indent => 0).gsub("\n","")
end

File.open(new_path, "wb") {|f| f.write(buffer.string) }

Configuration

Existing Files

By default, rubyzip will not overwrite files if they already exist inside of the extracted path. To change this behavior, you may specify a configuration option like so:

Zip.on_exists_proc = true

If you're using rubyzip with rails, consider placing this snippet of code in an initializer file such as config/initializers/rubyzip.rb

Additionally, if you want to configure rubyzip to overwrite existing files while creating a .zip file, you can do so with the following:

Zip.continue_on_exists_proc = true

Non-ASCII Names

If you want to store non-english names and want to open them on Windows(pre 7) you need to set this option:

Zip.unicode_names = true

Sometimes file names inside zip contain non-ASCII characters. If you can assume which encoding was used for such names and want to be able to find such entries using find_entry then you can force assumed encoding like so:

Zip.force_entry_names_encoding = 'UTF-8'

Allowed encoding names are the same as accepted by String#force_encoding

Date Validation

Some zip files might have an invalid date format, which will raise a warning. You can hide this warning with the following setting:

Zip.warn_invalid_date = false

Size Validation

By default (in rubyzip >= 2.0), rubyzip's extract method checks that an entry's reported uncompressed size is not (significantly) smaller than its actual size. This is to help you protect your application against zip bombs. Before extracting an entry, you should check that its size is in the range you expect. For example, if your application supports processing up to 100 files at once, each up to 10MiB, your zip extraction code might look like:

MAX_FILE_SIZE = 10 * 1024**2 # 10MiB
MAX_FILES = 100
Zip::File.open('foo.zip') do |zip_file|
  num_files = 0
  zip_file.each do |entry|
    num_files += 1 if entry.file?
    raise 'Too many extracted files' if num_files > MAX_FILES
    raise 'File too large when extracted' if entry.size > MAX_FILE_SIZE
    entry.extract
  end
end

If you need to extract zip files that report incorrect uncompressed sizes and you really trust them not too be too large, you can disable this setting with

Zip.validate_entry_sizes = false

Note that if you use the lower level Zip::InputStream interface, rubyzip does not check the entry sizes. In this case, the caller is responsible for making sure it does not read more data than expected from the input stream.

Compression level

When adding entries to a zip archive you can set the compression level to trade-off compressed size against compression speed. By default this is set to the same as the underlying Zlib library's default (Zlib::DEFAULT_COMPRESSION), which is somewhere in the middle.

You can configure the default compression level with:

Zip.default_compression = X

Where X is an integer between 0 and 9, inclusive. If this option is set to 0 (Zlib::NO_COMPRESSION) then entries will be stored in the zip archive uncompressed. A value of 1 (Zlib::BEST_SPEED) gives the fastest compression and 9 (Zlib::BEST_COMPRESSION) gives the smallest compressed file size.

This can also be set for each archive as an option to Zip::File:

Zip::File.open('foo.zip', create:true, compression_level: 9) do |zip|
  zip.add ...
end

Zip64 Support

Since version 3.0, Zip64 support is enabled for writing by default. To disable it do this:

Zip.write_zip64_support = false

Prior to version 3.0, Zip64 support is disabled for writing by default.

NOTE: If Zip64 write support is enabled then any extractor subsequently used may also require Zip64 support to read from the resultant archive.

Block Form

You can set multiple settings at the same time by using a block:

  Zip.setup do |c|
    c.on_exists_proc = true
    c.continue_on_exists_proc = true
    c.unicode_names = true
    c.default_compression = Zlib::BEST_COMPRESSION
  end

Compatibility

Rubyzip is known to run on a number of platforms and under a number of different Ruby versions.

Version 2.3.x

Rubyzip 2.3 is known to work on MRI 2.4 to 3.1 on Linux and Mac, and JRuby and Truffleruby on Linux. There are known issues with Windows which have been fixed on the development branch. Please let us know if you know Rubyzip 2.3 works on a platform/Ruby combination not listed here, or raise an issue if you see a failure where we think it should work.

Next (version 3.0.0)

Please see the table below for what we think the current situation is. Note: an empty cell means "unknown", not "does not work".

OS/Ruby 2.5 2.6 2.7 3.0 3.1 3.2 3.3 Head JRuby 9.4.6.0 JRuby Head Truffleruby 23.1.2 Truffleruby Head
Ubuntu 22.04 CI CI CI CI CI CI CI ci CI ci CI ci
Mac OS 12.7.3 CI x x ci ci ci ci ci x x
Windows 10 x
Windows Server 2022 CI CI mswin
CI ucrt

Key: CI - tested in CI, should work; ci - tested in CI, might fail; x - known working; o - known failing.

Ruby 3.0+ are also tested separately with YJIT turned on.

See the Actions tab in GitHub for full details.

Please raise a PR if you know Rubyzip works on a platform/Ruby combination not listed here, or raise an issue if you see a failure where we think it should work.

Developing

Install the dependencies:

bundle install

Run the tests with rake:

rake

Please also run rubocop over your changes.

Our CI runs on GitHub Actions. Please note that rubocop is run as part of the CI configuration and will fail a build if errors are found.

Website and Project Home

http://github.com/rubyzip/rubyzip

http://rdoc.info/github/rubyzip/rubyzip/master/frames

Authors

See https://github.com/rubyzip/rubyzip/graphs/contributors for a comprehensive list.

Current maintainers

  • Robert Haines (@hainesr)
  • John Lees-Miller (@jdleesmiller)
  • Oleksandr Simonov (@simonoff)

Original author

  • Thomas Sondergaard

License

Rubyzip is distributed under the same license as Ruby. In practice this means you can use it under the terms of the Ruby License or the 2-Clause BSD License. See https://www.ruby-lang.org/en/about/license.txt and LICENSE.md for details.

Research notice

Please note that this repository is participating in a study into sustainability of open source projects. Data will be gathered about this repository for approximately the next 12 months, starting from June 2021.

Data collected will include number of contributors, number of PRs, time taken to close/merge these PRs, and issues closed.

For more information, please visit our informational page or download our participant information sheet.

rubyzip's People

Contributors

2potatocakes avatar aeroastro avatar alor avatar aussiegeek avatar bbuchalter avatar byroot avatar feuda avatar hainesr avatar henkesn avatar jdleesmiller avatar johnnyshields avatar jspanjers avatar jstanley0 avatar justinlove avatar kachick avatar kellydunn avatar koic avatar matsu911 avatar mattr- avatar metavida avatar mnaberez avatar olleolleolle avatar orien avatar petergoldstein avatar shockwavenn avatar simonoff avatar taichi-ishitani avatar weshatheleopard avatar xaviershay avatar zacstewart avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rubyzip's Issues

Error no such file using ZipOutputStream

Here's my code:

tmp = Tempfile.new("#{request.remote_ip}-#{Time.now.strftime("%Y-%m-%d-%H%M%S")}.zip")
filenames = []
begin
  Zip::ZipOutputStream.open(tmp.path) do |zos|
    files.each do |file|
      # ensure unique filenames so they aren't overwritten
      filename = File.basename(file)
      i = 1
      while filenames.include?(filename)
        filename = File.basename(file) + i.to_s
        i += 1
      end
      filenames << filename

      zos.put_next_entry(filename)
      zos.print open(file).read
    end
  end
  send_file(tmp.path, :type => 'application/zip', :filename => "User_#{params[:id]}_Tender_#{params[:tender]}.zip")
ensure
  tmp.close
  tmp.unlink
end

And here's the error I'm getting:

!! Unexpected error while processing request: No such file or directory - /var/folders/yt/m06sk6nj4bg8ppzs4nmw8k2r0000gn/T/127.0.0.1-2013-06-17-161959.zip20130617-3292-k5ls06

using version 0.9.9

Test failure with rubyzip 0.9.9

When running the tests with ruby 1.8.7 (2012-03-02 patchlevel 359) [x86_64-linux] I am getting the following test failure:

  1) Failure:
test_compound1(ZipFileTest)
    [/var/tmp/portage/dev-ruby/rubyzip-0.9.9/work/ruby18/aussiegeek-rubyzip-7260506/test/ziptest.rb:1465:in `assert_contains'
     /var/tmp/portage/dev-ruby/rubyzip-0.9.9/work/ruby18/aussiegeek-rubyzip-7260506/test/ziptest.rb:1403:in `test_compound1']:
entry data/generated/randomAscii1.txt not in data/generated/randomBinary1.bin, data/generated/empty_chmod640.txt, data/generated/longAscii.txt, renamedName, data/generated/empty.txt, data/generated/short.txt, data/generated/randomBinary2.bin in zip file 5entry_copy.zip.
<false> is not true.

Zip consistency problem while reading eocd structure

rubyzip fails to extract zip files because of exception mentioned in the title.

Every other tool I ve tried so far is able to extract these zip files.

Might it be possible to somehow get it work with rubyzip as well?

Thanks a lot in advance.

Feature Request: initialize from IO (File, StringIO) in addition to string path

Sometimes my zip documents aren't on my filesystem. Whether they're persisted to a database or available from an HTTP server, persisting them to my filesystem so I can use rubyzip is a step that causes the need for additional cleanup & additional points of failure. A possible solution would be to make Zip work with any instance of IO, to the point of being able to provide an instance of IO (such as File or StringIO) to the initialize method.

interaction with sinatra fails

after upgrading from rubyzip 0.9.1 to rubyzip 0.9.5 I receive an error in the output from thin when it tries to make a request to the second thin instance that is using sinatra. I have tried two versions of sinatra and three versions of thin and the error only appears when making the upgrade to rubyzip. Not sure what other information would be useful.

Error that appears
!! Unexpected error while processing request: undefined method `starts_with' for "SERVER_NAME":String

StringIO

Anyone want StringIO support?

When rubyzip is used to modify a DOCX file, the resulting file can't be opened

Take a DOCX file (which is just a ZIP file with a different extension). Use Zip::ZipFile to remove one file from the archive, and replace it with an exact, identical one. The resulting file won't be openable by Microsoft Word, OpenOffice, etc.

require "zip/zipfilesystem"
f1 = File.binread("test.docx")
Zip::ZipFile.open("test.docx") { |z| s = z.file.read("word/document.xml"); z.file.delete("word/document.xml"); z.open("word/document.xml","w") { |f| f.write(s) } }
f2 = File.binread("test.docx")
f1 == f2 => false
f1.bytes.take(15)
=> [80, 75, 3, 4, 20, 0, 6, 0, 8, 0, 0, 0, 33, 0, 111]
f2.bytes.take(15)
=> [80, 75, 3, 4, 10, 0, 0, 0, 8, 0, 0, 0, 33, 0, 111]

After the ZIP file is read and written by rubyzip, a couple of bytes in the header are changed. I don't know enough about the ZIP format to understand why. But when I do the same thing with a standalone ZIP utility, it doesn't do that, and the resulting DOCX file can be opened properly.

Best way do it with new API?

I have a Rails controller method:

require 'zip/zip'

  def config_zip 
    tempfile = Tempfile.new('config_zip')
    Zip::ZipOutputStream.open(tempfile.path) do |z|
      z.put_next_entry("pass.txt")
      z.print 'Hello, Hello'
    end
    send_file tempfile.path, type: 'application/zip',
              disposition: 'attachment',
              filename: 'config.zip'
    tempfile.close
  end

It works perfect with 0.9.9 What the best way do the same with new api?

zip archive broken (CRC Error) on Windows when using utf-8 encoded string on Ruby 1.9.x

this code will reproduce the problem in the following environments:

  1. ruby-1.9.3-p0 + MacOS X 10.7.3 + rubyzip 0.9.6.1
  2. ruby-1.9.3-p125 + Windows 7 + rubyzip 0.9.6.1

'test_broken_on_1.9.3.zip' seems OK on MacOS, but can't extract on Windows 7 or Windows XP because of CRC error.

# -*- coding: utf-8 -*-
Zip::ZipOutputStream.open('test_broken_on_1.9.3.zip') {
  |zos|
  zos.put_next_entry('test.txt')
  zos << '日本語 Japanese Character in text.'
}

Zip::ZipOutputStream.open('test_not_broken.zip') {
  |zos|
  zos.put_next_entry('test.txt')
  zos << '日本語 Japanese Character in text.'.force_encoding('us-ascii')
}

Zip V1 compatibility

Suddenly i've met problem what there is problems with some legacy clients.

Problem is what recent version of ruby-zip produce zip, which require at least zip "v2" library to unzip them.

$ file legacy.zip 
legacy.zip: Zip archive data, at least v1.0 to extract

$ file modern.zip 
sync.zip: Zip archive data, at least v2.0 to extract

legacy.zip produced by ruby-zip 0.9.4 (November 2009)
modern.zip produced by ruby-zip 0.9.9 (lastest one)

with practically same code

Is there any way to enfore v1 compatibility? Is it worth to implement?

Zip64 extensions

I'm working on a project that uses rubyzip to unzip files. The files I need to unzip have recently grown to over 4GB in size, making them unreadable with the current version of rubyzip. I'm contemplating adding Zip64 extensions to deal with this limitation. Read capability is my immediate need, and probably what would be initially implemented. Has any work/planning/discussion happened regarding Zip64 in this project? Thanks.

rubyzip seems to break tempfile

tempfile_bugfixed.rb breaks mini_magick (3.3). MiniMagick create method creates a tempfile like this:

 tempfile = Tempfile.new(['mini_magick', ext.to_s.downcase])

According to the Tempfile docs this should work fine as passing an array for basename is supported: http://ruby-doc.org/stdlib/libdoc/tempfile/rdoc/classes/Tempfile.html

However, rubyzip's tempfile implementation generates a wrong file path, it doesn't seem to support an array:

/var/folders/ps/psLZoFzgHKSiegJINYpXAU+++TI/-Tmp-/["mini_magick", ""]47744-559602 (example)

Tag All Releases

E.g., tag v0.9.7 and v0.9.8, so that we can diff them by their tags and see what changed in the latest release.

Filename too long

Here's the stack trace:

File name too long - /private/var/apps/markpond/releases/20130510121012/tmp/archive-zip/1/e2?url=http:%2F%2Fwww.aeonmagazine.com%2Fworld-views%2Fwill-wiles-technology-new-aesthetic%2F&title=The+machine+gaze&description=As+the+boundaries+between+digital+and+physical+dissolve,+can+the+New+Aesthetic+help+us+see+things+more+clearly?.html20130510-95137-8f42nk.lock
/Users/alex/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/tempfile.rb:346:in `rmdir'
/Users/alex/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/tempfile.rb:346:in `rmdir'
/Users/alex/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/tempfile.rb:338:in `ensure in locking'
/Users/alex/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/tempfile.rb:338:in `locking'
/Users/alex/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/tempfile.rb:144:in `block in initialize'
/Users/alex/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/tmpdir.rb:133:in `create'
/Users/alex/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/tempfile.rb:134:in `initialize'
/var/apps/markpond/shared/bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_streamable_stream.rb:5:in `new'
/var/apps/markpond/shared/bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_streamable_stream.rb:5:in `initialize'
/var/apps/markpond/shared/bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_file.rb:157:in `new'
/var/apps/markpond/shared/bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_file.rb:157:in `get_output_stream'
/private/var/apps/markpond/releases/20130510121012/lib/zip_lib.rb:31:in `block in writeEntries'
/private/var/apps/markpond/releases/20130510121012/lib/zip_lib.rb:22:in `each'
/private/var/apps/markpond/releases/20130510121012/lib/zip_lib.rb:22:in `writeEntries'
/private/var/apps/markpond/releases/20130510121012/lib/zip_lib.rb:29:in `block in writeEntries'
/private/var/apps/markpond/releases/20130510121012/lib/zip_lib.rb:22:in `each'
/private/var/apps/markpond/releases/20130510121012/lib/zip_lib.rb:22:in `writeEntries'
/private/var/apps/markpond/releases/20130510121012/lib/zip_lib.rb:29:in `block in writeEntries'
/private/var/apps/markpond/releases/20130510121012/lib/zip_lib.rb:22:in `each'
/private/var/apps/markpond/releases/20130510121012/lib/zip_lib.rb:22:in `writeEntries'
/private/var/apps/markpond/releases/20130510121012/lib/zip_lib.rb:14:in `write'

Not sure why this is an issue, given that OS X had no problem handling this kind of length of file outside Ruby. Also, interesting how it had no problem creating the file - it was the deleting (rmdir) that threw the error.

Avoid iconv on 1.9

When requiring rubyzip on 1.9.3, the following warning is issued:

.rvm/rubies/ruby-1.9.3-preview1/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in require': iconv will be deprecated in the future, use String#encode instead.`

LoadError (cannot load such file -- zip/zip)

As of version 1.0.0 getting "LoadError (cannot load such file -- zip/zip)" when trying to launch our rails app both locally and on Heroku. App now won't start. Note that we don't use rubyzip directly . . . it is loaded as an axlsx gem dependency.

Worked fine on 0.9.9.

Rolling back to 0.9.9 for now.

Ben

TypeError: can't dup NilClass

I've been seeing the following stack trace when trying to create and write to a new zip file.

It happens sporadically, ie. not every single time, but enough to worry me. Wondering if someone could shed some like on it?

bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_entry_set.rb:44:in `dup'
bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_entry_set.rb:44:in `block in dup'
bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_entry_set.rb:44:in `map'
bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_entry_set.rb:44:in `dup'
bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_file.rb:75:in `initialize'
bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_file.rb:228:in `commit'
bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_file.rb:242:in `close'
bundle/ruby/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_file.rb:92:in `open'

Zip::ZipFile#mkdir changes input variable

When you run this code, the @name variable changes:

Zip::ZipFile.open(archive_path, Zip::ZipFile::CREATE) do |zipfile|
  zipfile.dir.mkdir @name
end

For example, if @name is "my_dir" before the code, it will be "my_dir/" after.

ZipInputStream broken

The following code only shows the first real file in the attached zip. The rest is not shown anymore.
Zip file created on Mac OSX.

Zip::ZipInputStream::open("eins.zip") do |io|
while (entry = io.get_next_entry)
pp entry
end
end

old zip files work.

but

Zip::ZipFile::foreach("eins.zip") do |entry|
 pp entry.name
end

works for the same file.

`ZipFile.open` changes file permissions when combined with `Tempfile`

This is unexpected behavior and caused some trouble with file permission errors on serving files from rails through apache.

Replicated here:

# ruby-2.0.0-p247
require 'tempfile'
require 'zip/zip' # rubyzip (0.9.9)

def print_and_chmod(file)
  puts sprintf("%o", File.stat(file.path).mode)
  file.chmod(0640)
  puts sprintf("after file.chmod: %o", File.stat(file.path).mode)
  File.chmod(0640, file.path)
  puts sprintf("after File.chmod: %o", File.stat(file.path).mode)
end

File.write('foo.txt', 'foo.txt')

FileUtils.mkdir_p 'temporary_dir'
Tempfile.open('foo.zip', 'temporary_dir') do |tempfile|
  puts 'Before ZipOutputStream.open, Before ZipFile.open:'
  print_and_chmod(tempfile)

  Zip::ZipOutputStream.open(tempfile) { |zos| } # make sure it's a zip

  puts 'After ZipOutputStream.open, Before ZipFile.open:'
  print_and_chmod(tempfile)

  Zip::ZipFile.open(tempfile.path, Zip::ZipFile::CREATE) do |zip|
    zip.add(tempfile.path, 'foo.txt')
  end

  puts 'After ZipFile.open:'
  print_and_chmod(tempfile)
end

FileUtils.rm('foo.txt')

Which displays:

Before ZipOutputStream.open, Before ZipFile.open:
100600
after file.chmod: 100640
after File.chmod: 100640
After ZipOutputStream.open, Before ZipFile.open:
100640
after file.chmod: 100640
after File.chmod: 100640
After ZipFile.open:
100600
after file.chmod: 100600
after File.chmod: 100640

If you'd like me to turn this into a test for the suite, I'd be more than willing. I just need to know where to add it.

Temp files created in get_output_stream not unlinked

I have an issue when I am using rubyzip in a daemon and a lot of tempfiles are creeated (and persist) in the folder where I am creating the zip files.

This seems to be because the temp files which are created are not explicitly unlinked and remain on the disk until the ruby process ends or the tempfile objects are garbage collected.

Add missing Zip::Entry arguments to Zip::File#get_output_stream

Using Zip::File#get_output_stream with a non-Zip::Entry instance as entry argument you cannot specify the other Zip::Entry arguments, like for example compression method.

This monkey patch allows it:

require 'zip'

module Zip
  class File
    def get_output_stream(entry, permissionInt = nil, comment = nil, extra = nil, compressed_size = nil, crc = nil, compression_method = nil, size = nil, time = nil,  &aProc)
      newEntry =
        if entry.kind_of?(Entry)
          entry
        else
          Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, size, time)
        end

      if newEntry.directory?
        raise ArgumentError,
              "cannot open stream to directory entry - '#{newEntry}'"
      end
      newEntry.unix_perms = permissionInt
      zipStreamableEntry  = StreamableStream.new(newEntry)
      @entry_set << zipStreamableEntry
      zipStreamableEntry.get_output_stream(&aProc)
    end
  end
end

# example
Zip::File.open('test.zip', Zip::File::CREATE) do |archive|
  # enter a non compressed entry 
  archive.get_output_stream("first.txt", nil, nil, nil, nil, nil, Zip::Entry::STORED) { |f| f.puts "Hello from ZipFile" }
end

File renaming is profoundly broken

Create a directory called test.
create four files inside that directory called: a, b, c, d
zip that directory with: zip -r test.zip test/

then execute this script:

  Zip::File.open('test.zip', "wb") do |z|
    z.each do |f|
      puts f.name
      z.rename f, "Z" + f.name
    end
  end

  puts "OUTPUT:

  Zip::File.open('test.zip', "wb") do |z|
    z.each do |f|
      puts f.name
    end
  end

the result is:

test/
test/b
test/d
Ztest/b
ZZtest/b
OUTPUT:
test/a
test/c
Ztest/
Ztest/d
ZZZtest/b

it seems that the entry are mangled while iterating over them with the z.each()

Unicode filenames do not work in Windows

I’m using Rubyzip 0.9.9 to create a zip archives containing filenames with accented characters on Mac OS and Linux computers. When I then extract those archives on Windows computers (using WinRar, 7-Zip or Windows’ built-in support for “compressed folders”), the filenames get mangled – most accented characters are replaced by 2 other characters, so it looks like the filenames are encoded as UTF-8 but interpreted by Windows as single byte encoding.

I found #3, but as far as I see, this pull request has never been merged.

Is this a known issue? Are there any workarounds?

If it’s possible to fix this with a sane amount of work I’d be glad to help – any pointers appreciated! 😄

RubyZip not working on heroku

I am working with creating zip file after read users cv's. Code is working fine on local

machine. But there is error when deploy this code on heroku.

My code is below

require ''zip"

                   def  get_stream(contractors)
                       # contractors are all user records
                       t = Tempfile.new("#{Rails.root}/tmp/my-temp-filename-#{Time.now}") #this line on heroku break
                      begin
                          Zip::OutputStream.open(t.path) do |zos|
                            contractors.each_with_index do |contractor, index|
                            if contractor.cv.path
                                zos.put_next_entry("#{index+1}_# {contractor.full_name.parameterize.underscore}.#  {contractor.cv_file_name.split(".").last}")
                                zos.print IO.read(contractor.cv.path)
                           end
                       end
                       end
                       send_file t.path, :type => 'application/zip', :filename => "Contractors_cvs.zip", :x_sendfile => true
                      t.close
                  rescue
                    t.close
                  end

               end

while heroku logs --app just show me "Completed 500 Internal Server Error in 56ms"

Any help please.

Thanks

no such file to load -- zip/ziprequire

Gemfile:

gem 'rubyzip', :require => ['zip/zip', 'zip/zipfilesystem', 'zip/ziprequire']

Useful Version numbers:
jruby 1.7.0.preview1 (ruby-1.9.3-p203)
rubyzip (0.9.8)

The error I am getting is "no such file to load -- zip/ziprequire" when running the tests for my app. I don't see this file in lib/zip like I do for zipfilesystem.rb and zip.rb. Was ziprequire removed?

rails 3 production - LoadError (cannot load such file -- zip/zip)

I'm using rubyzip to zip a csv file so uses can download it. This works perfectly in development mode. But when I tried zipping the file on the production server (rackspace) I received the error: LoadError (cannot load such file -- zip/zip). Is it a path issue? Anyone know a fix?

The error is being called in my code on this line: require 'zip/zip'

Another more concise way to add a directory recursively to a zip file

I've discovered this technique for recursively adding a directory to a zip file:

http://grosser.it/2009/02/04/compressing-a-folder-to-a-zip-archive-with-ruby/

The essence of it is:

Dir["#{path}/**/**"].each do |file|
  zipfile.add(file.sub(path+'/',''),file)
end

This loops over every entry in the subdirectory (recursively) and adds it in replicating the directory names in the zip file. Thought it would make for a more concise example in your samples section.

RubyZip Crashes on Windows

Hi

We use RubyZip 0.9.9 on Windows and it crashes with the following error - we do have enough memory on this machine. The same works just fine on Linux.

   C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-0.9.9/lib/zip/inflater.rb:14:in `sysread': failed to allocate memory (NoMemoryError)
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_input_stream.rb:106:in `sysread'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-0.9.9/lib/zip/ioextras.rb:59:in `read'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/oddb2xml-1.1.2/lib/oddb2xml/downloader.rb:154:in `block (2 levels) in download'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_entry.rb:502:in `get_input_stream'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/oddb2xml-1.1.2/lib/oddb2xml/downloader.rb:154:in `block in download'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_entry_set.rb:35:in `each'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_entry_set.rb:35:in `each'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_central_directory.rb:109:in `each'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_file.rb:132:in `block in foreach'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_file.rb:90:in `open'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-0.9.9/lib/zip/zip_file.rb:131:in `foreach'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/oddb2xml-1.1.2/lib/oddb2xml/downloader.rb:152:in `download'
    from C:/Ruby193/lib/ruby/gems/1.9.1/gems/oddb2xml-1.1.2/lib/oddb2xml/cli.rb:32:in `block in run'

Any hints?

Best
Zeno

Disable extra fields?

Hi, all. Is there an easy way to disable the extra fields entirely? What I'm looking for is something similar to what you get when using the -X option with command-line zip.

I'm trying to create epub documents. An epub container is just a zip file, but unfortunately the extra file attributes are not supported (this is arguably broken, but it's not within my control). zip -X works fine, but rubyzip doesn't.

cannot set the file permission

the file permissions are always set to 0644 disregarding the value assigned to a file.
example:

filename = 'test'
zipfile = 'test.zip'

Zip::ZipFile.open(zipfile, Zip::ZipFile::CREATE) do |z|
z.file.open(filename, "w") { |f| f.write "created by rubyzip" }
z.file.chmod(0755, filename)
end

Zip::ZipFile.open(zipfile) do |z|
puts "perms: %o" % z.file.stat(filename).mode
end

outputs => perms: 100644
instead of 755

any suggestion?

thank you

split feature is non-compliant

The split feature implemented in Pull request 75 doesn't conform to the standard, in that:

  1. Local and central directory header records must never be split across a segment boundary. (8.5.2) The rubyzip split code doesn't even look at the zip file content; it just blindly chops its up into fixed-size pieces.
  2. The central directory entry for each file should indicate a disk number where the file starts (4.3.12); this is hard-coded to 0 in rubyzip.
  3. The end-of-central-directory record should indicate disk numbers where the central directory begins and ends, and also the number of entries located on the last disk in addition to the total number of entries. (4.3.16) rubyzip is again hard-coded to assume there is only one disk.

(section numbers refer to version 6.5.2 of http://www.pkware.com/documents/casestudies/APPNOTE.TXT)

In addition, the test code doesn't test whether anyone might be able to read the split archive; it merely strings the pieces back together and tests reading the reconstituted file. The zip specification is, however, designed not to require stringing pieces of split archives together--or even generating a single big archive to begin with, as fogeys like me who remember spanning archives across floppy disks would know. It's designed so that the central directory and any particular file can be located in-place in their segment (or "disk"). rubyzip doesn't accomplish this.

License

Hi, is your license the same as Ruby 1.8 (GPLv2+ or Ruby) or Ruby 1.9 (BSD or Ruby)?

Thanks for the clarification,
Bohuslav.

Add tests into gem

Hi,

It would be very nice to put the tests on the gem. I work with Gentoo and ruby and we use the tests to look for problems on a gem's installation.

On Ruby 2.0: `read': can't set length of shared string

[Copy of http://sourceforge.net/tracker/?func=detail&aid=3498506&group_id=43107&atid=435170 -- I'm really confused where stuff is supposed to go and what's the official repo?]

I realize this is not quite a "proper" bug report, but this is as far as I got tracking down this issue -- I'm thinking it's a problem in rubyzip:

$ rbenv install 2.0.0-dev    # ruby 2.0.0dev (2012-03-07 trunk 34944) [x86_64-linux]
$ RBENV_VERSION=2.0.0-dev gem install selenium-webdriver
$ RBENV_VERSION=2.0.0-dev ruby -e 'require "selenium/webdriver";Selenium::WebDriver.for(:firefox, {})'
/home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/inflater.rb:42:in `read': can't set length of shared string (RuntimeError)
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/inflater.rb:42:in `internal_produce_input'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/inflater.rb:14:in `sysread'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_input_stream.rb:106:in `sysread'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_entry.rb:581:in `block (2 levels) in write_file'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_entry.rb:502:in `get_input_stream'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_entry.rb:577:in `block in write_file'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_entry.rb:576:in `open'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_entry.rb:576:in `write_file'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_entry.rb:205:in `extract'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_file.rb:188:in `extract'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/common/zipper.rb:27:in `block (2 levels) in unzip'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_entry_set.rb:31:in `each'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_entry_set.rb:31:in `each'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_central_directory.rb:113:in `each'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/common/zipper.rb:22:in `block in unzip'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/rubyzip-0.9.6.1/lib/zip/zip_file.rb:86:in `open'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/common/zipper.rb:21:in `unzip'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/firefox/extension.rb:38:in `create_root'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/firefox/extension.rb:17:in `write_to'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/firefox/profile.rb:193:in `block in install_extensions'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/firefox/profile.rb:191:in `each'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/firefox/profile.rb:191:in `install_extensions'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/firefox/profile.rb:76:in `layout_on_disk'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/firefox/launcher.rb:57:in `create_profile'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/firefox/launcher.rb:34:in `block in launch'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/firefox/socket_lock.rb:20:in `locked'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/firefox/launcher.rb:32:in `launch'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/firefox/bridge.rb:19:in `initialize'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/common/driver.rb:31:in `new'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver/common/driver.rb:31:in `for'
from /home/ubuntu/.rbenv/versions/2.0.0-dev/lib/ruby/gems/2.0.0/gems/selenium-webdriver-2.20.0/lib/selenium/webdriver.rb:61:in `for'
from -e:1:in `<main>'

The offending line reads:

@zlibInflater.inflate(@inputStream.read(Decompressor::CHUNK_SIZE, buf))

Zlib::GzipWriter object must be closed explicitly

I'm using the Zip::ZipFile.open method to create a zip file. My log ends up showing the following…

zlib(finalizer): Zlib::GzipWriter object must be closed explicitly.
zlib(finalizer): the stream was freed prematurely.

I'm not really sure what's going on, particularly because I was under the impression that using the block variant handled closing everything up.

Error when uses Zip::ZipFile.open_buffer()

Hi,

I have the content of a zip file in memory and I need to get the content of some files.

This is a dummy example:

content = Assets.first.zip_content

zip = Zip::ZipFile.open_buffer(content)   

And I got the following error:

/Users/ricardohsd/Work/Github/rubyzip/lib/zip/zip_entry.rb:212:in `read_c_dir_entry': undefined method `path' for #<StringIO:0x007faa4b859d30> (NoMethodError)
    from /Users/ricardohsd/Work/Github/rubyzip/lib/zip/zip_central_directory.rb:79:in `block in read_central_directory_entries'
    from /Users/ricardohsd/Work/Github/rubyzip/lib/zip/zip_central_directory.rb:78:in `times'
    from /Users/ricardohsd/Work/Github/rubyzip/lib/zip/zip_central_directory.rb:78:in `read_central_directory_entries'
    from /Users/ricardohsd/Work/Github/rubyzip/lib/zip/zip_central_directory.rb:86:in `read_from_stream'
    from /Users/ricardohsd/Work/Github/rubyzip/lib/zip/zip_file.rb:116:in `open_buffer'
    from teste.rb:15:in `<main>'

There is another way to solve this problem?

Would you like some documentation help?

I like rubyzip, but had some initial trouble figuring it out. Once I got it working for my purposes, I wrote a short tutorial, which has been helpful for some others. My examples were intentionally silly, but I'd be happy to add something similar (but less silly if you like) to the README here.

Here's my blog post: http://sleeplessgeek.blogspot.com/2011/03/short-rubyzip-tutorial.html

And here's a library of my own, as another example of my documentation: github.com/nathanl/authority

Would you like a contribution to your README?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.