Class: Omnizip::Formats::Zip::Reader

Inherits:
Object
  • Object
show all
Includes:
Constants
Defined in:
lib/omnizip/formats/zip/reader.rb

Overview

ZIP archive reader

Constant Summary

Constants included from Constants

Constants::ATTR_ARCHIVE, Constants::ATTR_DIRECTORY, Constants::CENTRAL_DIRECTORY_SIGNATURE, Constants::COMPRESSION_BZIP2, Constants::COMPRESSION_DEFLATE, Constants::COMPRESSION_DEFLATE64, Constants::COMPRESSION_IMPLODED, Constants::COMPRESSION_LZMA, Constants::COMPRESSION_PPMD, Constants::COMPRESSION_REDUCED_1, Constants::COMPRESSION_REDUCED_2, Constants::COMPRESSION_REDUCED_3, Constants::COMPRESSION_REDUCED_4, Constants::COMPRESSION_SHRUNK, Constants::COMPRESSION_STORE, Constants::COMPRESSION_ZSTANDARD, Constants::DATA_DESCRIPTOR_SIGNATURE, Constants::END_OF_CENTRAL_DIRECTORY_SIGNATURE, Constants::FLAG_DATA_DESCRIPTOR, Constants::FLAG_ENCRYPTED, Constants::FLAG_STRONG_ENCRYPTION, Constants::FLAG_UTF8, Constants::LOCAL_FILE_HEADER_SIGNATURE, Constants::MAX_COMMENT_LENGTH, Constants::UNIX_DIR_PERMISSIONS, Constants::UNIX_EXTRA_FIELD_TAG, Constants::UNIX_FILE_PERMISSIONS, Constants::UNIX_SYMLINK_PERMISSIONS, Constants::VERSION_BZIP2, Constants::VERSION_DEFAULT, Constants::VERSION_DEFLATE, Constants::VERSION_LZMA, Constants::VERSION_MADE_BY_UNIX, Constants::VERSION_MADE_BY_WINDOWS, Constants::VERSION_ZIP64, Constants::ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIGNATURE, Constants::ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE, Constants::ZIP64_EXTRA_FIELD_TAG, Constants::ZIP64_LIMIT

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file_path) ⇒ Reader

Returns a new instance of Reader.



14
15
16
17
18
# File 'lib/omnizip/formats/zip/reader.rb', line 14

def initialize(file_path)
  @file_path = file_path
  @entries = []
  @central_directory = []
end

Instance Attribute Details

#file_pathObject (readonly)

Returns the value of attribute file_path.



12
13
14
# File 'lib/omnizip/formats/zip/reader.rb', line 12

def file_path
  @file_path
end

Instance Method Details

#decompress(compressed_data, method, uncompressed_size) ⇒ String

Decompress compressed_data according to method. Public API for callers (e.g., ParallelExtractor) that hold raw compressed payload outside an entry stream.

Parameters:

  • compressed_data (String)

    Compressed bytes

  • method (Integer)

    ZIP compression method code

  • uncompressed_size (Integer)

    Expected uncompressed size

Returns:

  • (String)

    Decompressed bytes



132
133
134
# File 'lib/omnizip/formats/zip/reader.rb', line 132

def decompress(compressed_data, method, uncompressed_size)
  decompress_data(compressed_data, method, uncompressed_size)
end

#entriesObject

All entries, parsing on first use — a Reader is usable immediately; #read remains for explicit eager loading



41
42
43
44
# File 'lib/omnizip/formats/zip/reader.rb', line 41

def entries
  ensure_read
  @entries
end

#extract_all(output_dir, preserve_links: true, dereference_links: false) ⇒ Object

Extract all files to a directory



47
48
49
50
51
52
53
# File 'lib/omnizip/formats/zip/reader.rb', line 47

def extract_all(output_dir, preserve_links: true,
dereference_links: false)
  entries.each do |entry|
    extract_entry(entry, output_dir, preserve_links: preserve_links,
                                     dereference_links: dereference_links)
  end
end

#extract_entry(entry, output_dir, preserve_links: true, dereference_links: false) ⇒ Object

Extract a specific entry



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/omnizip/formats/zip/reader.rb', line 56

def extract_entry(entry, output_dir, preserve_links: true,
dereference_links: false)
  output_path = File.join(output_dir, entry.filename)

  if entry.directory?
    FileUtils.mkdir_p(output_path)
  elsif preserve_links && !dereference_links && entry.symlink?
    extract_symlink(entry, output_dir)
  else
    FileUtils.mkdir_p(File.dirname(output_path))

    decompressed_data = read_entry_data(entry)

    # Write decompressed data
    File.binwrite(output_path, decompressed_data)

    # Set file permissions if Unix
    if entry.unix_permissions.positive?
      File.chmod(entry.unix_permissions & 0o777, output_path)
    end
  end
end

Extract a symbolic link



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/omnizip/formats/zip/reader.rb', line 80

def extract_symlink(entry, output_dir)
  output_path = File.join(output_dir, entry.filename)

  unless LinkHandler.symlink_supported?
    warn "Warning: Symbolic links not supported on #{RUBY_PLATFORM}, extracting as regular file"
    extract_entry(entry, output_dir, preserve_links: false)
    return
  end

  target = entry.link_target
  unless target
    warn "Warning: No link target found for #{entry.filename}, skipping"
    return
  end

  FileUtils.mkdir_p(File.dirname(output_path))

  # Remove existing file/link if present
  FileUtils.rm_f(output_path) if File.exist?(output_path) || File.symlink?(output_path)

  LinkHandler.create_symlink(target, output_path)
end

#list_entries(show_links: false) ⇒ Object

List all entries in the archive



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/omnizip/formats/zip/reader.rb', line 104

def list_entries(show_links: false)
  entries.map do |entry|
    info = {
      filename: entry.filename,
      compressed_size: entry.compressed_size,
      uncompressed_size: entry.uncompressed_size,
      compression_method: compression_method_name(entry.compression_method),
      crc32: entry.crc32,
      directory: entry.directory?,
    }

    if show_links && entry.symlink?
      info[:symlink] = true
      info[:link_target] = entry.link_target
    end

    info
  end
end

#readObject

Read and parse the ZIP archive



21
22
23
24
25
26
# File 'lib/omnizip/formats/zip/reader.rb', line 21

def read
  File.open(file_path, "rb") do |io|
    read_from_io(io)
  end
  self
end

#read_entry(entry_name) ⇒ String

Read an entry's decompressed content into a String. Directory entries return an empty string; symlinks return their target path.

Parameters:

  • entry_name (String)

    Name of the entry to read

Returns:

  • (String)

    Decompressed content

Raises:

  • (RuntimeError)

    if the entry is not found



143
144
145
146
147
148
149
150
151
152
# File 'lib/omnizip/formats/zip/reader.rb', line 143

def read_entry(entry_name)
  ensure_read
  entry = @entries.find { |e| e.filename == entry_name }
  raise Errno::ENOENT, "Entry not found: #{entry_name}" unless entry

  return "" if entry.directory?
  return entry.link_target if entry.symlink?

  read_entry_data(entry)
end

#read_from_io(io) ⇒ Object

Read from an IO object



29
30
31
32
33
34
35
36
37
# File 'lib/omnizip/formats/zip/reader.rb', line 29

def read_from_io(io)
  # Find and read End of Central Directory
  eocd = EndOfCentralDirectory.find_in_file(io)

  # Read Central Directory
  read_central_directory(io, eocd)

  self
end