class Xlsxrb::Ooxml::ZipReader
Reads ZIP archives using only stdlib (zlib). Supports streaming entry decompression with bounded memory consumption.
Constants
- LOCAL_HEADER_SIG
- MAX_UNCOMPRESSED_SIZE
Attributes
max_uncompressed_size
[RW]
untyped
Public Class Methods
(untyped io, ?max_uncompressed_size: Integer, ?should_close_io: bool) → void
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 245 def initialize(io, max_uncompressed_size: MAX_UNCOMPRESSED_SIZE, should_close_io: false) @raw_io = io @max_uncompressed_size = max_uncompressed_size @should_close_io = should_close_io @tempfile = nil @io = nil @catalog = nil @ci_catalog = nil @cached_entries = nil end
(untyped source, ?max_uncompressed_size: Integer) ?{ (ZipReader) → untyped } → (ZipReader | untyped)
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 222 def self.open(source, max_uncompressed_size: MAX_UNCOMPRESSED_SIZE) is_binary = source.is_a?(String) && (source.start_with?(LOCAL_HEADER_SIG) || source.include?("\x00")) should_close = source.is_a?(String) && !is_binary && File.file?(source) io = if should_close File.open(source, "rb") elsif source.is_a?(String) StringIO.new(source.b) else source end reader = new(io, max_uncompressed_size: max_uncompressed_size, should_close_io: should_close) if block_given? begin yield reader ensure reader.close end else reader end end
Opens a ZIP from a file path or IO and yields the reader.
Public Instance Methods
() → void
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 358 def close @raw_io.close if @should_close_io && @raw_io.respond_to?(:close) && !@raw_io.closed? if @tempfile @tempfile.close @tempfile.unlink @tempfile = nil end nil end
Closes the archive reader and cleans up any temporary resources.
(String entry_name, ZipWriter writer, ?target_path: String?) → void
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 335 def copy_to_writer(entry_name, writer, target_path: nil) entry = lookup_entry(entry_name) raise ArgumentError, "Entry not found in archive: #{entry_name}" unless entry if entry.compressed_size.zero? && entry.uncompressed_size.positive? data = read_entry(entry_name) writer.add_binary_entry(target_path || entry_name, data || "") return end writer.copy_raw_entry( target_path || entry_name, io, data_offset: entry.data_offset, compressed_size: entry.compressed_size, uncompressed_size: entry.uncompressed_size, crc32: entry.crc32, method: entry.method ) end
Copies the specified entry to a ZipWriter directly without decompression.
() ?{ (String, String?) → void } → (void | Enumerator[[String, String?], void])
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 285 def each_entry(&block) return enum_for(:each_entry) unless block catalog.each_key do |name| block.call(name, read_entry(name)) end end
Yields (entry_name, data_string) for each file in the archive.
(String entry_name, ?chunk_size: Integer) ?{ (String) → void } → (void | Enumerator[String, void])
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 304 def each_entry_chunk(entry_name, chunk_size: 65_536, &block) return enum_for(:each_entry_chunk, entry_name, chunk_size: chunk_size) unless block stream = open_entry_io(entry_name) begin stream.each_chunk(chunk_size, &block) ensure stream.close end end
Yields inflated binary chunks directly from the compressed stream.
(String name) → bool
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 317 def entry?(name) !lookup_entry(name).nil? end
Returns true if the archive contains the specified entry.
() → Hash[String, Entry]
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 323 def entry_catalog catalog end
Returns entry metadata catalog.
() → Array[String]
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 329 def entry_names catalog.keys end
Returns array of all entry names.
(String entry_name) → EntryStreamIO
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 295 def open_entry_io(entry_name) entry = lookup_entry(entry_name) raise ArgumentError, "Entry not found in archive: #{entry_name}" unless entry EntryStreamIO.new(io, entry.data_offset, entry.compressed_size, entry.method, max_uncompressed_size: @max_uncompressed_size) end
Opens an EntryStreamIO for an entry by name.
() → Hash[String, String?]
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 258 def read_all result = {} catalog.each_key do |name| result[name] = read_entry(name) end result end
Returns a Hash { entry_name => raw_bytes } for all entries.
(String name) → String?
Source
# File lib/xlsxrb/ooxml/zip_reader.rb, line 268 def read_entry(name) norm_name = normalize_entry_name(name) cached = @cached_entries[norm_name] if @cached_entries&.key?(norm_name) entry = lookup_entry(norm_name) return nil unless entry cached ||= @cached_entries[entry.name] if @cached_entries&.key?(entry.name) return normalize_text_encoding(cached) if cached io.seek(entry.data_offset, IO::SEEK_SET) raw = io.read(entry.compressed_size) decompress(raw, entry.method) end
Returns raw bytes for a single entry, or nil if not found.