lib/io_streams/zip/reader.rb in iostreams-0.20.1 vs lib/io_streams/zip/reader.rb in iostreams-0.20.2

- old
+ new

@@ -1,70 +1,82 @@ module IOStreams module Zip class Reader # Read from a zip file or stream, decompressing the contents as it is read # The input stream from the first file found in the zip file is passed - # to the supplied block + # to the supplied block. # + # Parameters: + # entry_file_name: [String] + # Name of the file within the Zip file to read. + # Default: Read the first file found in the zip file. + # # Example: # IOStreams::Zip::Reader.open('abc.zip') do |io_stream| # # Read 256 bytes at a time # while data = io_stream.read(256) # puts data # end # end - def self.open(file_name_or_io, _ = nil, &block) - if !defined?(JRuby) && !defined?(::Zip) - # MRI needs Ruby Zip, since it only has native support for GZip - begin - require 'zip' - rescue LoadError => exc - raise(LoadError, "Install gem 'rubyzip' to read and write Zip files: #{exc.message}") - end - end - + def self.open(file_name_or_io, entry_file_name: nil, &block) # File name supplied - return read_file(file_name_or_io, &block) unless IOStreams.reader_stream?(file_name_or_io) + return read_file(file_name_or_io, entry_file_name, &block) unless IOStreams.reader_stream?(file_name_or_io) - # ZIP can only work against a file, not a stream, so create temp file. + # Ruby ZIP gem uses `#seek` so can only work against a file, not a stream, so create temp file. + # JRuby ZIP requires an InputStream. IOStreams::File::Path.temp_file_name('iostreams_zip') do |temp_file_name| IOStreams.copy(file_name_or_io, temp_file_name, target_options: {streams: []}) - read_file(temp_file_name, &block) + read_file(temp_file_name, entry_file_name, &block) end end if defined?(JRuby) # Java has built-in support for Zip files - def self.read_file(file_name, &block) + def self.read_file(file_name, entry_file_name) fin = Java::JavaIo::FileInputStream.new(file_name) zin = Java::JavaUtilZip::ZipInputStream.new(fin) - zin.get_next_entry - block.call(zin.to_io) + + get_entry(zin, entry_file_name) || + raise(Java::JavaUtilZip::ZipException.new("File #{entry_file_name} not found within zip file.")) + + yield(zin.to_io) ensure zin.close if zin fin.close if fin end else - # Read from a zip file or stream, decompressing the contents as it is read # The input stream from the first file found in the zip file is passed # to the supplied block - def self.read_file(file_name, &block) - begin - zin = ::Zip::InputStream.new(file_name) - zin.get_next_entry - block.call(zin) - ensure + def self.read_file(file_name, entry_file_name) + if !defined?(::Zip) + # MRI needs Ruby Zip, since it only has native support for GZip begin - zin.close if zin - rescue IOError - # Ignore file already closed errors since Zip::InputStream - # does not have a #closed? method + require 'zip' + rescue LoadError => exc + raise(LoadError, "Install gem 'rubyzip' to read and write Zip files: #{exc.message}") end end + + ::Zip::InputStream.open(file_name) do |zin| + get_entry(zin, entry_file_name) || + raise(::Zip::EntryNameError, "File #{entry_file_name} not found within zip file.") + yield(zin) + end end + end + def self.get_entry(zin, entry_file_name) + if entry_file_name.nil? + zin.get_next_entry + return true + end + + while entry = zin.get_next_entry + return true if entry.name == entry_file_name + end + false end end end end