class LibZip::File
A zip archive, open for reading, writing, or both.
An archive is a transaction: entries added, removed and renamed are noted as you go, and close writes them to disk. The block form of ::open commits on normal exit and throws the archive away if the block fails, so a failed write can’t leave a truncated zip behind.
LibZip::File.open("archive.zip", create: true) do |zip| zip.add("a.txt", "a.txt") zip.get_output_stream("b.txt") { |out| out.write("from memory") } end LibZip::File.open("archive.zip") do |zip| zip.names # => ["a.txt", "b.txt"] zip.read("b.txt") # => "from memory" end
File includes Enumerable over its LibZip::Entry records, so map, select and the rest work on a listing.
Once the archive is closed, all of these fail with LibZip::EntryError.
Public Class Methods
Source
# File doc/api.rb, line 76 def self.open(path, create: false, password: nil) # :yields: zip end
Opens the archive at path.
With create the archive is created when path doesn’t exist; an existing file is opened as it is and isn’t truncated, so create is safe to pass whenever you intend to write. Without it, a missing path fails with LibZip::NotFoundError.
password becomes the archive default: the password each read of an encrypted entry falls back to. See password=, and the call-level password of read and get_input_stream.
Without a block you get the archive and you call close yourself. With a block the archive is yielded; on normal exit it’s closed and committed and the block’s value comes back from ::open. If the block fails, the archive is thrown away: no bytes are written, and an archive that ::open had just created is removed from disk.
LibZip::File.open("archive.zip") # read
LibZip::File.open("archive.zip", create: true) # read and write
LibZip::File.open("secrets.zip", password: "hunter2") { |zip| ... }
A 2nd argument that isn’t a Hash is a TypeError.
Public Instance Methods
Source
# File doc/api.rb, line 113 def add(name, source_path, encryption: nil, password: nil) end
Adds source_path from disk as the entry name, and returns self, so calls chain.
zip.add("a.txt", "a.txt").add("docs/b.txt", "b.txt")
The file is read when the archive is written, not now, so a source that disappears, or that can’t be read any more, shows up as an error from close. An entry of the same name is replaced.
encryption is one of :aes128, :aes192, :aes256, :none or nil, and needs a password, either here or as the archive default. :pkware (ZipCrypto) isn’t allowed for writing.
Fails with LibZip::NotFoundError when source_path doesn’t exist, LibZip::PermissionError when it can’t be read, and LibZip::InvalidArgumentError when it isn’t a regular file, when password comes without encryption, or when encryption isn’t one of the symbols above.
Source
# File doc/api.rb, line 88 def close end
Writes the central directory, flushes the archive to disk, and releases it. Live input streams on this archive are closed first.
Returns nil. Closing an already closed archive fails with LibZip::EntryError, so a double close is a visible bug.
An archive that was created and then not written to produces no file: libzip won’t write an empty archive, and removing the last remaining entry deletes the file instead.
Source
# File doc/api.rb, line 275 def comment end
The archive comment, as a UTF-8 String, or nil when the archive has none. A comment that isn’t valid UTF-8 comes back as ASCII-8BIT, instead of as a broken String.
Source
# File doc/api.rb, line 282 def comment=(comment) end
Sets the archive comment, which close writes. nil clears it.
A comment longer than 65535 bytes, the limit of the field in the zip format, fails with LibZip::InvalidArgumentError.
Source
# File doc/api.rb, line 220 def each # :yields: entry end
Passes each LibZip::Entry to the block, and returns the full Array. Without a block, returns an Enumerator.
This is what File’s Enumerable methods run on.
zip.map(&:name) zip.select(&:directory?)
Source
Source
# File doc/api.rb, line 210 def entries end
Returns the central directory as an Array of LibZip::Entry snapshots.
No extraction happens, and no entry bytes are read. The array is built fresh on each call, so it reflects entries added or removed since the previous one.
Source
# File doc/api.rb, line 246 def find_entry(name) end
Returns the LibZip::Entry named name, or nil when the archive has no such entry. See get_entry to fail instead.
Source
# File doc/api.rb, line 252 def get_entry(name) end
Returns the LibZip::Entry named name, or fails with LibZip::NotFoundError. See find_entry to get nil instead.
Source
# File doc/api.rb, line 146 def get_input_stream(name, password: nil) # :yields: stream end
Returns a LibZip::InputStream over the entry name, which pulls the entry in chunks instead of building 1 String.
zip.get_input_stream("big.csv") do |stream| stream.read(64 * 1024) until stream.eof? end
With a block the stream is yielded and closed on the way out, including when the block fails, and the block’s value comes back. Without one you get the stream; close it with LibZip::InputStream#close, or let close on the archive do it.
password works as in read. Fails with LibZip::NotFoundError when there’s no such entry.
Source
# File doc/api.rb, line 166 def get_output_stream(name, encryption: nil, password: nil) # :yields: stream end
Returns a LibZip::OutputStream that writes the entry name from memory.
zip.get_output_stream("data.csv") do |out| out.write("a,b,c\n") out << "1,2,3\n" end
The stream buffers what you give it and adds the entry only when it’s closed. A stream left to the garbage collector, or one where the block failed, contributes no entry, while entries written before it remain. An entry of the same name is replaced.
With a block the stream is yielded, committed on normal exit, and the block’s value comes back. Without one you get the stream, and LibZip::OutputStream#close commits it.
encryption and password work as in add.
Source
# File doc/api.rb, line 269 def glob(pattern) # :yields: entry end
Returns the LibZip::Entry records matching pattern, and passes each of them to the block if one is given.
zip.glob("*.txt") # => the .txt entries at the top level zip.glob("**/*.txt") # => the .txt entries at any depth zip.glob("docs") # => the "docs/" entry
Matching is ::File.fnmatch? with FNM_PATHNAME, FNM_DOTMATCH and FNM_EXTGLOB: * doesn’t cross a /, ** matches 0 or more directories, ?, [...] and {a,b} all work, and a leading dot isn’t special. A directory entry matches without the trailing / it’s stored with.
Unlike Dir.glob on macOS, matching is case-sensitive on all platforms.
Source
# File doc/api.rb, line 241 def include?(name) end
Whether name is an entry of this archive. Accepts a String or a LibZip::Entry.
Source
# File doc/api.rb, line 230 def names end
Returns the entry names as an Array of UTF-8 Strings, directory entries included, with the trailing / they’re stored with.
Source
# File doc/api.rb, line 301 def password=(password) end
Sets the archive default password: the one used for an encrypted entry read with no password argument. nil clears it.
There’s no reader; a password that goes in doesn’t come back out.
Zip encryption covers entry contents only. Names, comments and the central directory remain in the clear whatever the password.
Source
# File doc/api.rb, line 129 def read(name, password: nil) end
Loads the entry name and returns its bytes as an ASCII-8BIT String.
password decrypts this one entry and overrides the archive default, so an incorrect password here fails even when the archive was opened with the correct one.
Fails with LibZip::NotFoundError when there’s no such entry, LibZip::PasswordError when the password is missing or incorrect, LibZip::DecompressionError when the data doesn’t match its CRC or its AES HMAC, and LibZip::CorruptArchiveError when the entry is shorter or longer than the size in the central directory.
Use get_input_stream for an entry too large to keep in memory.
Source
# File doc/api.rb, line 185 def remove(name) end
Deletes an entry, named by String or by the LibZip::Entry, and returns it as a snapshot taken before the deletion, so its metadata remains readable after the deletion and after the archive closes.
removed = zip.remove("old.txt") removed.size # => 1234
The entry disappears from the listing at once; close writes the file again. Removing the last remaining entry deletes the archive from disk instead of writing an empty one.
Fails with LibZip::NotFoundError when the entry isn’t in the archive, including on a 2nd remove of the same name, and LibZip::InvalidArgumentError for a LibZip::Entry belonging to another archive. There’s no 3rd outcome: you get the entry back, or you get an exception.
Source
# File doc/api.rb, line 202 def rename(old_name, new_name) end
Renames an entry, named by String or by the LibZip::Entry, and returns a snapshot taken after the rename. Size, CRC, time and compression are preserved.
zip.rename("draft.txt", "final.txt")
A rename moves 1 record: the children of a directory entry keep the names they were stored with, whatever the directory is called now.
Fails with LibZip::NotFoundError when old_name isn’t in the archive, LibZip::AlreadyExistsError when new_name already is, and LibZip::InvalidArgumentError when 1 name has the trailing / of a directory entry and the other doesn’t, or for a LibZip::Entry belonging to another archive.
Source
# File doc/api.rb, line 291 def set_comment(name, comment) end
Sets the comment of the entry name, and returns self. nil clears it. Read it back with LibZip::Entry#comment.
The comment is stored as UTF-8 when its bytes are valid UTF-8, and as raw bytes otherwise. Fails with LibZip::NotFoundError when there’s no such entry, and LibZip::InvalidArgumentError past the 65535-byte limit.