Class: XlsxKit::ZipWriter

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

Overview

最小化 ZIP32 写入器,专为 XLSX 打包设计。

与 ZipReader 对应,不依赖 rubyzip,仅支持写入 XLSX 所需的两种压缩方式:

  • DEFLATE (method 8): 通用压缩
  • STORED (method 0): 无压缩

实现思路:

  1. add_entry 时即时写出 Local File Header + 数据,并记录偏移
  2. 同时在内存累积 Central Directory 记录
  3. close 时写出 Central Directory + EOCD 收尾

边写边压,避免将整个 ZIP 缓冲在内存或磁盘。

Constant Summary collapse

LOCAL_SIG =

ZIP 格式签名

"PK\x03\x04".b.freeze
CDENTRY_SIG =

Local File Header

"PK\x01\x02".b.freeze
EOCD_SIG =

Central Directory File Header

"PK\x05\x06".b.freeze
STORED =

压缩方法

0
DEFLATE =
8

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io) ⇒ ZipWriter

初始化写入器。

Parameters:

  • io (IO) —

    可写 IO(必须已以二进制模式打开)



49
50
51
52
53
54
# File 'lib/xlsxkit/zip_writer.rb', line 49

def initialize(io)
  @io      = io
  @offset  = 0          # 当前数据起始偏移(用于 Central Directory 定位)
  @count   = 0          # 条目数
  @central = ''.b       # 中央目录累积缓冲
end

Class Method Details

.open(path) ⇒ Object

便捷类方法:写入文件路径,块结束后自动收尾并关闭。



58
59
60
61
62
63
64
# File 'lib/xlsxkit/zip_writer.rb', line 58

def self.open(path)
  File.open(path, 'wb') do |f|
    writer = new(f)
    yield writer
    writer.close
  end
end

Instance Method Details

#add_entry(name, data, method: DEFLATE) ⇒ Object

添加一个条目(文件),立即写出数据。

Parameters:

  • name (String) —

    条目名(如 "xl/worksheets/sheet1.xml")

  • data (String) —

    内容(UTF-8 字符串或二进制)

  • method (Integer) (defaults to: DEFLATE) —

    压缩方式(DEFLATE / STORED)



72
73
74
75
76
77
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
# File 'lib/xlsxkit/zip_writer.rb', line 72

def add_entry(name, data, method: DEFLATE)
  name_bytes = name.b
  data_bytes = data.b
  crc = Zlib.crc32(data_bytes)

  compressed =
    case method
    when STORED  then data_bytes
    when DEFLATE then Zlib::Deflate.new(nil, -Zlib::MAX_WBITS).deflate(data_bytes, Zlib::FINISH)
    else raise ArgumentError, "unsupported compression method: #{method}"
    end

  # Local File Header (30 bytes + name + data)
  local = LOCAL_SIG.dup
  local << [20, 0, method, 0, 0, crc,
            compressed.bytesize, data_bytes.bytesize,
            name_bytes.bytesize, 0].pack('vvvvvVVVvv')
  local << name_bytes
  local << compressed
  @io.write(local)

  # Central Directory Record (46 bytes + name)
  cd = CDENTRY_SIG.dup
  cd << [20, 20, 0, method, 0, 0, crc,
         compressed.bytesize, data_bytes.bytesize,
         name_bytes.bytesize, 0, 0, 0, 0, 0, @offset].pack('vvvvvvVVVvvvvvVV')
  cd << name_bytes
  @central << cd

  @offset += local.bytesize
  @count += 1
  self
end

#close ⇒ Object

写出 Central Directory 与 EOCD,收尾 ZIP 文件。



108
109
110
111
112
113
114
115
116
# File 'lib/xlsxkit/zip_writer.rb', line 108

def close
  cd_offset = @offset
  @io.write(@central)

  eocd = EOCD_SIG.dup
  eocd << [0, 0, @count, @count, @central.bytesize, cd_offset, 0].pack('vvvvVVv')
  @io.write(eocd)
  @io
end