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.



25
26
27
28
29
# File 'lib/xlsxrb/ooxml/zip_writer.rb', line 25

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

Class Method Details

.open(target, &block) ⇒ Object



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

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).



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

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.



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

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



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

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



78
79
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
# File 'lib/xlsxrb/ooxml/zip_writer.rb', line 78

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.



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

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



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

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