Class: ZipTricks::Microzip
- Inherits:
-
Object
- Object
- ZipTricks::Microzip
- Includes:
- Bytesize
- Defined in:
- lib/zip_tricks/microzip.rb
Overview
A replacement for RubyZip for streaming, with a couple of small differences. The first difference is that it is verbosely-written-to-the-spec and you can actually follow what is happening. It does not support quite a few fancy features of Rubyzip, but instead it can be digested in one reading, and has solid Zip64 support. It also does not attempt any tricks with Zip64 placeholder extra fields because the ZipTricks streaming engine assumes you know how large your file is (both compressed and uncompressed) and you have the file's CRC32 checksum upfront.
Just like Rubyzip it will switch to Zip64 automatically if required, but there is no global setting to enable that behavior - it is always on.
Defined Under Namespace
Modules: Bytesize
Constant Summary collapse
- STORED =
0- DEFLATED =
8- TooMuch =
Class.new(StandardError)
- DuplicateFilenames =
Class.new(StandardError)
- UnknownMode =
Class.new(StandardError)
Instance Method Summary collapse
-
#add_local_file_header(io:, filename:, crc32:, compressed_size:, uncompressed_size:, storage_mode:, mtime: Time.now.utc) ⇒ void
Adds a file to the entry list and immediately writes out it's local file header into the output stream.
-
#initialize ⇒ Microzip
constructor
Creates a new streaming writer.
-
#write_central_directory(io) ⇒ void
Writes the central directory (including the Zip6 salient bits if necessary).
Methods included from Bytesize
Constructor Details
#initialize ⇒ Microzip
Creates a new streaming writer. The writer is stateful and knows it's list of ZIP file entries as they are being added.
246 247 248 249 |
# File 'lib/zip_tricks/microzip.rb', line 246 def initialize @files = [] @local_header_offsets = [] end |
Instance Method Details
#add_local_file_header(io:, filename:, crc32:, compressed_size:, uncompressed_size:, storage_mode:, mtime: Time.now.utc) ⇒ void
This method returns an undefined value.
Adds a file to the entry list and immediately writes out it's local file header into the output stream.
262 263 264 265 266 267 268 269 270 271 |
# File 'lib/zip_tricks/microzip.rb', line 262 def add_local_file_header(io:, filename:, crc32:, compressed_size:, uncompressed_size:, storage_mode:, mtime: Time.now.utc) if @files.any?{|e| e.filename == filename } raise DuplicateFilenames, "Filename #{filename.inspect} already used in the archive" end raise UnknownMode, "Unknown compression mode #{storage_mode}" unless [STORED, DEFLATED].include?(storage_mode) e = Entry.new(filename, crc32, compressed_size, uncompressed_size, storage_mode, mtime) @files << e @local_header_offsets << io.tell e.write_local_file_header(io) end |
#write_central_directory(io) ⇒ void
This method returns an undefined value.
Writes the central directory (including the Zip6 salient bits if necessary)
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
# File 'lib/zip_tricks/microzip.rb', line 278 def write_central_directory(io) start_of_central_directory = io.tell # Central directory file headers, per file in order @files.each_with_index do |file, i| local_file_header_offset_from_start_of_file = @local_header_offsets.fetch(i) file.write_central_directory_file_header(io, local_file_header_offset_from_start_of_file) end central_dir_size = io.tell - start_of_central_directory zip64_required = central_dir_size > FOUR_BYTE_MAX_UINT || start_of_central_directory > FOUR_BYTE_MAX_UINT || @files.length > TWO_BYTE_MAX_UINT || @files.any?(&:requires_zip64?) # Then, if zip64 is used if zip64_required # [zip64 end of central directory record] zip64_eocdr_offset = io.tell # zip64 end of central dir io << [0x06064b50].pack(C_V) # signature 4 bytes (0x06064b50) io << [44].pack(C_Qe) # size of zip64 end of central # directory record 8 bytes # (this is ex. the 12 bytes of the signature and the size value itself). # Without the extensible data sector it is always 44. io << MADE_BY_SIGNATURE # version made by 2 bytes io << [VERSION_NEEDED_TO_EXTRACT_ZIP64].pack(C_v) # version needed to extract 2 bytes io << [0].pack(C_V) # number of this disk 4 bytes io << [0].pack(C_V) # number of the disk with the # start of the central directory 4 bytes io << [@files.length].pack(C_Qe) # total number of entries in the # central directory on this disk 8 bytes io << [@files.length].pack(C_Qe) # total number of entries in the # central directory 8 bytes io << [central_dir_size].pack(C_Qe) # size of the central directory 8 bytes # offset of start of central # directory with respect to io << [start_of_central_directory].pack(C_Qe) # the starting disk number 8 bytes # zip64 extensible data sector (variable size) # [zip64 end of central directory locator] io << [0x07064b50].pack("V") # zip64 end of central dir locator # signature 4 bytes (0x07064b50) io << [0].pack(C_V) # number of the disk with the # start of the zip64 end of # central directory 4 bytes io << [zip64_eocdr_offset].pack(C_Qe) # relative offset of the zip64 # end of central directory record 8 bytes # (note: "relative" is actually "from the start of the file") io << [1].pack(C_V) # total number of disks 4 bytes end # Then the end of central directory record: io << [0x06054b50].pack(C_V) # end of central dir signature 4 bytes (0x06054b50) io << [0].pack(C_v) # number of this disk 2 bytes io << [0].pack(C_v) # number of the disk with the # start of the central directory 2 bytes if zip64_required # the number of entries will be read from the zip64 part of the central directory io << [TWO_BYTE_MAX_UINT].pack(C_v) # total number of entries in the # central directory on this disk 2 bytes io << [TWO_BYTE_MAX_UINT].pack(C_v) # total number of entries in # the central directory 2 bytes else io << [@files.length].pack(C_v) # total number of entries in the # central directory on this disk 2 bytes io << [@files.length].pack(C_v) # total number of entries in # the central directory 2 bytes end if zip64_required io << [FOUR_BYTE_MAX_UINT].pack(C_V) # size of the central directory 4 bytes io << [FOUR_BYTE_MAX_UINT].pack(C_V) # offset of start of central # directory with respect to # the starting disk number 4 bytes else io << [central_dir_size].pack(C_V) # size of the central directory 4 bytes io << [start_of_central_directory].pack(C_V) # offset of start of central # directory with respect to # the starting disk number 4 bytes end io << [0].pack(C_v) # .ZIP file comment length 2 bytes # .ZIP file comment (variable size) end |