Class: Xlsxrb::Ooxml::ZipWriter

Inherits:
Object
  • Object
show all
Defined in:
lib/xlsxrb/ooxml/zip_writer.rb

Overview

Writes ZIP archives using only stdlib (zlib). Supports streaming: entries are written sequentially, central directory at close.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io) ⇒ ZipWriter

Returns a new instance of ZipWriter.



27
28
29
30
31
# File 'lib/xlsxrb/ooxml/zip_writer.rb', line 27

def initialize(io)
  @io = io
  @entries = []
  @closed = false
end

Class Method Details

.open(target, &block) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/xlsxrb/ooxml/zip_writer.rb', line 12

def self.open(target, &block)
  io = target.is_a?(String) ? File.open(target, "wb") : target
  writer = new(io)
  if block
    begin
      yield writer
    ensure
      writer.close
      io.close if target.is_a?(String)
    end
  else
    writer
  end
end

Instance Method Details

#add_binary_entry(path, content) ⇒ Object

Add a file entry with binary content (no encoding conversion).



42
43
44
45
46
47
# File 'lib/xlsxrb/ooxml/zip_writer.rb', line 42

def add_binary_entry(path, content)
  raise "ZipWriter is closed" if @closed

  content_bytes = content.b
  write_raw_entry(path, content_bytes)
end

#add_entry(path, content) ⇒ Object

Add a file entry with string content.



34
35
36
37
38
39
# File 'lib/xlsxrb/ooxml/zip_writer.rb', line 34

def add_entry(path, content)
  raise "ZipWriter is closed" if @closed

  content_bytes = content.encode("UTF-8").b
  write_raw_entry(path, content_bytes)
end

#closeObject



111
112
113
114
115
116
117
118
119
120
# File 'lib/xlsxrb/ooxml/zip_writer.rb', line 111

def close
  return if @closed

  finish_entry if @current_entry

  @closed = true
  cd_offset = @io.is_a?(StringIO) ? @io.pos : @io.tell
  cd_size = write_central_directory
  write_end_of_central_directory(cd_offset, cd_size)
end

#finish_entryObject



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/xlsxrb/ooxml/zip_writer.rb', line 80

def finish_entry
  raise "No entry started" unless @current_entry

  entry = @current_entry
  @current_entry = nil

  # Flush remaining deflate data
  remaining = entry[:deflater].finish
  entry[:compressed_size] += remaining.bytesize
  @io.write(remaining)
  entry[:deflater].close

  final_crc = entry[:crc] & 0xFFFFFFFF

  # Patch the local header if seekable
  current_pos = @io.is_a?(StringIO) ? @io.pos : @io.tell
  if @io.respond_to?(:seek)
    @io.seek(entry[:offset])
    write_local_header(entry[:path], final_crc, entry[:compressed_size], entry[:uncompressed_size])
    @io.seek(current_pos)
  end

  @entries << {
    path: entry[:path],
    crc32: final_crc,
    compressed_size: entry[:compressed_size],
    uncompressed_size: entry[:uncompressed_size],
    offset: entry[:offset]
  }
end

#start_entry(path) ⇒ Object

Write a string directly into the current ZIP entry stream. Use start_entry / write_data / finish_entry for true streaming.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/xlsxrb/ooxml/zip_writer.rb', line 51

def start_entry(path)
  raise "ZipWriter is closed" if @closed

  @current_entry = {
    path: path,
    offset: @io.is_a?(StringIO) ? @io.pos : @io.tell,
    deflater: Zlib::Deflate.new(Zlib::DEFAULT_COMPRESSION, -Zlib::MAX_WBITS),
    crc: Zlib.crc32,
    uncompressed_size: 0,
    compressed_size: 0
  }

  # Write a placeholder local header (will be patched later if IO supports seek)
  write_local_header(path, 0, 0, 0)
end

#write_data(str) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/xlsxrb/ooxml/zip_writer.rb', line 67

def write_data(str)
  raise "No entry started" unless @current_entry

  bytes = str.b
  return if bytes.empty?

  @current_entry[:crc] = Zlib.crc32(bytes, @current_entry[:crc])
  @current_entry[:uncompressed_size] += bytes.bytesize
  compressed = @current_entry[:deflater].deflate(bytes, Zlib::SYNC_FLUSH)
  @current_entry[:compressed_size] += compressed.bytesize
  @io.write(compressed)
end