zip alternatives and similar packages
Based on the "zip" category.
Alternatively, view zip alternatives based on common mentions on social networks and blogs.
Do you think we are missing an alternative of zip or a related project?
- Why this library is written
- Quick start
This is a feature-rich, memory-efficient, and type-safe library to
manipulate Zip archives. The library is the most complete and efficient
implementation of the .ZIP specification in Haskell (at least from the
open-sourced ones). In particular, it's created with large multimedia data
in mind and provides all features users might expect, comparable in terms of
feature-set with libraries like
libzip in C.
Why this library is written
There are a few libraries to work with Zip archives, yet every one of them provides only a subset of useful functionality or otherwise is flawed in some way so it cannot be easily used in some situations. Let's examine all libraries available on Hackage to understand the motivation for this package.
zip-archive is a widely used library. It's quite old, well-known and
simple to use. However, it creates Zip archives purely, as
memory. This is not acceptable if you work with big data. For example, if
you have a collection of files with the total size 500 MB and you want to
pack them into an archive, you can easily consume up to 1 GB of memory (the
files plus the resulting archive). This is not always affordable. Even if
you want just to look at the list of archive entries it will read the entire
archive into memory.
This is a binding to the C library
libzip. There is always a
certain kind of trouble with bindings. For example, you need to ensure that
the target library is installed and its version is compatible with the
version of your binding.
It's not that bad with libraries that do not break their API for years, but
it's not the case with
libzip. As the maintainer of
LibZip puts it:
libzip 0.10, 0.11, and 1.0 are not binary compatible. If your C library is 0.11.x, then you should use LibZip 0.11. If your C library is 1.0, then you should use LibZip master branch (not yet released to Hackage).
Now, on my machine I have the version 1.0. To put the package on Stackage we had to use the version 0.10, because Stackage uses Ubuntu to build packages and libraries on Ubuntu are always ancient. This means that I cannot use the version of the library from Stackage, and I don't yet know what will be on the server.
After much frustration, I decided to avoid using
LibZip. After all, this
is not a project that shouldn't be done completely in Haskell. By rewriting
this in Haskell, I also can make it safer to use.
This one uses the right approach: leverage a good streaming library
conduit) for memory-efficient processing. The library is however not
feature-rich and has certain problems (including the programming style, it
error if an entry is missing in the archive, among other things),
some of them are reported on its issue tracker. It also does not appear to
be maintained (the last sign of activity was on December 23, 2014).
The library supports all features specified in the modern .ZIP specification except for encryption and multi-disk archives. See more about this below.
For reference, here is a copy of the specification.
zip supports the following compression methods:
The best way to add a new compression method to the library is to write a
conduit that will do the compression and publish it as a library.
then depend on it and add it to the list of supported compression methods.
The current list of compression methods reflects what is available on
Hackage at the moment.
Encryption is currently not supported. Encryption system described in the .ZIP specification is known to be seriously flawed, so it's probably not the best way to protect your data anyway. The encryption method seems to be a proprietary technology of PKWARE (at least that's what stated about it in the .ZIP specification), so to hell with it.
Sources of file data
The following sources are supported:
- File name. This is an efficient method to perform compression or decompression. You specify where to get data or where to save it and the rest is handled by the library.
- Conduit source or sink.
- ByteString. Use it only with small data.
- Copy file from another archive. An efficient operation, file is copied “as is”—no re-compression is performed.
When necessary, the
ZIP64 extension is automatically used. It's necessary
- The total size of the archive is greater than 4 GB.
- The size of a single compressed/uncompressed file in the archive is greater than 4 GB.
- There are more than 65535 entries in the archive.
The library is particularly well suited for processing large files. For example, I've been able to create 6.5 GB archive with reasonable speed and without significant memory consumption.
The library has an API that makes it impossible to create archive with non-portable or invalid file names in it.
As of .ZIP specification 6.3.2, files with Unicode symbols in their names can be stored in Zip archives. The library supports mechanisms for this and uses them automatically when needed. Besides UTF-8, CP437 is also supported as per the specification.
Meta-information about files
The library allows us to attach comments to the entire archive or individual files, and also gives its user full control over extra fields that are written to file headers, so the user can store arbitrary information about files in the archive.
Codec.Archive.Zip provides everything you may need to
manipulate Zip archives. There are three things that should be clarified
right away to avoid confusion.
First, we use the
EntrySelector type that can be obtained from relative
FilePaths (paths to directories are not allowed). This method may seem
awkward at first, but it will protect you from the problems with portability
when your archive is unpacked on a different platform.
Second, there is no way to add directories, or to be precise, empty directories to your archive. This approach is used in Git and I find it sane.
Finally, the third feature of the library is that it does not modify the
archive instantly, because doing so on every manipulation would often be
inefficient. Instead, we maintain a collection of pending actions that can
be turned into an optimized procedure that efficiently modifies the archive
in one pass. Normally, this should be of no concern to you, because all
actions are performed automatically when you leave the
If, however, you ever need to force an update, the
commit function is your
Let's take a look at some examples that show how to accomplish most common tasks.
To get full information about archive entries, use
λ> withArchive archivePath (M.keys <$> getEntries)
This will return a list of all entries in the archive at
possible to extract contents of an entry as a strict
λ> withArchive archivePath (getEntry entrySelector)
…to stream them to a given sink:
λ> withArchive archivePath (sourceEntry entrySelector mySink)
…to save a specific entry to a file:
λ> withArchive archivePath (saveEntry entrySelector pathToFile)
…and finally just unpack the entire archive into a directory:
λ> withArchive archivePath (unpackInto destDir)
Modifying is also easy. When you want to create a new archive use
withArchive will do. To add an entry from
λ> createArchive archivePath (addEntry Store "Hello, World!" entrySelector)
You can stream from
Source as well:
λ> createArchive archivePath (sinkEntry Deflate source entrySelector)
To add contents from a file, use
λ> let toSelector = const (mkEntrySelector "my-entry.txt") λ> createArchive archivePath (loadEntry BZip2 toSelector myFilePath)
Finally, you can copy an entry from another archive without re-compression
(unless you use
recompress, see below):
λ> createArchive archivePath (copyEntry srcArchivePath selector selector)
It's often desirable to just pack a directory:
λ> createArchive archivePath (packDirRecur Deflate mkEntrySelector dir)
It's also possible to:
- rename an entry with
- delete an entry with
- change compression method with
- change comment associated with an entry with
- delete comment with
- set modification time with
- manipulate extra fields with
- check if entry is intact with
- undo changes with
- force changes to be written to file system with
This should cover all your needs. Feel free to open an issue if you're missing something.
You can contact the maintainer via the issue tracker.
Pull requests are welcome.
Copyright © 2016–present Mark Karpov
Distributed under BSD 3 clause license.
*Note that all licence references and agreements mentioned in the zip README section above are relevant to that project's source code only.