class Xlsxrb::Ooxml::ZipWriter
Writes ZIP archives using only stdlib (zlib). Supports streaming: entries are written sequentially, central directory at close.
Public Class Methods
Source
# File lib/xlsxrb/ooxml/zip_writer.rb, line 25 def initialize(io) @io = io @entries = [] @closed = false end
# 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
Public Instance Methods
# 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 a file entry with binary content (no encoding conversion).
Source
# 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
Add a file entry with string content.
Source
# 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
Source
# 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
Source
# 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 a string directly into the current ZIP entry stream. Use start_entry / write_data / finish_entry for true streaming.
Source
# 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