Class: Omnizip::Zip::OutputStream

Inherits:
Object
  • Object
show all
Includes:
Formats::Zip::Constants
Defined in:
lib/omnizip/zip/output_stream.rb

Overview

Rubyzip-compatible OutputStream class Provides streaming write API for ZIP archives

Constant Summary

Constants included from Formats::Zip::Constants

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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file_path_or_io) ⇒ OutputStream

Initialize output stream

Parameters:

  • file_path_or_io (String, IO)

    File path or IO object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/omnizip/zip/output_stream.rb', line 32

def initialize(file_path_or_io)
  if file_path_or_io.is_a?(String)
    @file_path = file_path_or_io
    @io = ::File.open(file_path_or_io, "wb")
    @owns_io = true
  else
    @io = file_path_or_io
    @owns_io = false
  end

  @entries = []
  @current_entry = nil
  @current_entry_data = nil
  @closed = false
end

Class Method Details

.open(file_path) {|stream| ... } ⇒ OutputStream

Open a new output stream

Parameters:

  • file_path (String)

    Path to ZIP file

Yields:

  • (stream)

    Block to write to the stream

Returns:



16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/omnizip/zip/output_stream.rb', line 16

def self.open(file_path, &block)
  stream = new(file_path)

  if block
    begin
      yield(stream)
    ensure
      stream.close
    end
  else
    stream
  end
end

Instance Method Details

#closeObject

Close the stream



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/omnizip/zip/output_stream.rb', line 139

def close
  return if @closed

  close_entry if @current_entry

  # Write central directory
  central_directory_offset = @io.pos
  write_central_directory
  central_directory_size = @io.pos - central_directory_offset

  # Write end of central directory
  write_end_of_central_directory(central_directory_offset,
                                 central_directory_size)

  @io.close if @owns_io
  @closed = true
end

#close_entryObject

Close current entry



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/omnizip/zip/output_stream.rb', line 96

def close_entry
  return unless @current_entry

  # Compress data if needed
  compressed_data = compress_entry_data

  # Calculate CRC32
  crc32 = @current_entry[:directory] ? 0 : calculate_crc32(@current_entry_data)

  # Write compressed data
  @io.write(compressed_data) unless @current_entry[:directory]

  # Update entry info
  @current_entry.merge!({
                          crc32: crc32,
                          compressed_size: compressed_data.bytesize,
                          uncompressed_size: @current_entry_data.bytesize,
                        })

  # Update local file header with correct sizes
  current_pos = @io.pos
  @io.seek(@current_entry[:offset], ::IO::SEEK_SET)
  write_local_file_header
  @io.seek(current_pos, ::IO::SEEK_SET)

  # Save entry for central directory
  @entries << @current_entry

  @current_entry = nil
  @current_entry_data = nil
end

#closed?Boolean

Check if stream is closed

Returns:

  • (Boolean)


158
159
160
# File 'lib/omnizip/zip/output_stream.rb', line 158

def closed?
  @closed
end

#commentObject

Get archive comment



134
135
136
# File 'lib/omnizip/zip/output_stream.rb', line 134

def comment
  @comment || ""
end

#comment=(comment) ⇒ Object

Set archive comment



129
130
131
# File 'lib/omnizip/zip/output_stream.rb', line 129

def comment=(comment)
  @comment = comment
end

Print data to current entry



86
87
88
# File 'lib/omnizip/zip/output_stream.rb', line 86

def print(*args)
  write(args.join)
end

#put_next_entry(name, time: Time.now, comment: "", compression: :deflate, level: 6) ⇒ Object

Start a new entry

Parameters:

  • name (String)

    Entry name

  • time (Time) (defaults to: Time.now)

    Modification time

  • comment (String) (defaults to: "")

    Entry comment

  • compression (Symbol) (defaults to: :deflate)

    Compression method (:store or :deflate)

  • level (Integer) (defaults to: 6)

    Compression level (1-9)



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/omnizip/zip/output_stream.rb', line 54

def put_next_entry(name, time: Time.now, comment: "",
compression: :deflate, level: 6)
  close_entry if @current_entry

  @current_entry = {
    name: name,
    time: time,
    comment: comment,
    compression: compression_method_for(compression),
    level: level,
    offset: @io.pos,
    directory: name.end_with?("/"),
  }
  @current_entry_data = String.new(encoding: Encoding::BINARY)

  # Write placeholder local file header (will update later)
  write_local_file_header_placeholder

  self
end

#puts(*args) ⇒ Object

Put data to current entry



91
92
93
# File 'lib/omnizip/zip/output_stream.rb', line 91

def puts(*args)
  args.each { |arg| write("#{arg}\n") }
end

#write(data) ⇒ Object Also known as: <<

Write data to current entry

Parameters:

  • data (String)

    Data to write



77
78
79
80
81
82
# File 'lib/omnizip/zip/output_stream.rb', line 77

def write(data)
  raise "No entry started. Call put_next_entry first" unless @current_entry

  @current_entry_data << data.b
  self
end