{"id":15288122,"url":"https://github.com/mrkkrp/zip","last_synced_at":"2025-05-16T13:02:56.162Z","repository":{"id":3535640,"uuid":"49888196","full_name":"mrkkrp/zip","owner":"mrkkrp","description":"Efficient library for manipulating zip archives","archived":false,"fork":false,"pushed_at":"2025-03-27T20:09:03.000Z","size":370,"stargazers_count":82,"open_issues_count":10,"forks_count":28,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-09T08:04:25.942Z","etag":null,"topics":["compression","haskell","zip"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mrkkrp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-01-18T16:11:22.000Z","updated_at":"2025-04-07T13:17:15.000Z","dependencies_parsed_at":"2024-04-19T22:24:09.876Z","dependency_job_id":"6d81cc05-ea89-4f6b-a7ba-48ad6f9bb533","html_url":"https://github.com/mrkkrp/zip","commit_stats":{"total_commits":293,"total_committers":17,"mean_commits":"17.235294117647058","dds":"0.48805460750853247","last_synced_commit":"ee1d1440a390772c9da3c35d10b10ec6d9706848"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrkkrp%2Fzip","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrkkrp%2Fzip/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrkkrp%2Fzip/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrkkrp%2Fzip/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mrkkrp","download_url":"https://codeload.github.com/mrkkrp/zip/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254535826,"owners_count":22087398,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compression","haskell","zip"],"created_at":"2024-09-30T15:44:10.792Z","updated_at":"2025-05-16T13:02:56.130Z","avatar_url":"https://github.com/mrkkrp.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zip\n\n[![License BSD3](https://img.shields.io/badge/license-BSD3-brightgreen.svg)](http://opensource.org/licenses/BSD-3-Clause)\n[![Hackage](https://img.shields.io/hackage/v/zip.svg?style=flat)](https://hackage.haskell.org/package/zip)\n[![Stackage Nightly](http://stackage.org/package/zip/badge/nightly)](http://stackage.org/nightly/package/zip)\n[![Stackage LTS](http://stackage.org/package/zip/badge/lts)](http://stackage.org/lts/package/zip)\n[![CI](https://github.com/mrkkrp/zip/actions/workflows/ci.yaml/badge.svg)](https://github.com/mrkkrp/zip/actions/workflows/ci.yaml)\n\n* [Why this library was written](#why-this-library-was-written)\n    * [zip-archive](#zip-archive)\n    * [LibZip](#libzip)\n    * [zip-conduit](#zip-conduit)\n* [Features](#features)\n    * [Compression methods](#compression-methods)\n    * [Encryption](#encryption)\n    * [Sources of file data](#sources-of-file-data)\n    * [ZIP64](#zip64)\n    * [Filenames](#filenames)\n    * [Meta-information about files](#meta-information-about-files)\n* [Quick start](#quick-start)\n* [Contribution](#contribution)\n* [License](#license)\n\nThis is a feature-rich, memory-efficient, and type-safe library to\nmanipulate Zip archives. The library was created with large multimedia data\nin mind and provides all features users might expect, comparable in terms of\nfeature-set with libraries like `libzip` in C.\n\n## Why this library was written\n\nThere are a few libraries to work with Zip archives, yet every one of them\nprovides only a subset of useful functionality or otherwise is flawed in\nsome way so it cannot be easily used in some situations. Let's examine all\nlibraries available on Hackage to understand the motivation for this\npackage.\n\n### zip-archive\n\n`zip-archive` is a widely used library. It's quite old, well-known and\nsimple to use. However, it creates Zip archives purely, as `ByteStrings`s in\nmemory. This is not acceptable if you work with big data. For example, if\nyou have a collection of files with the total size 500 MB and you want to\npack them into an archive, you can easily consume up to 1 GB of memory (the\nfiles plus the resulting archive). This is not always affordable. Even if\nyou want just to look at the list of archive entries it will read the entire\narchive into memory.\n\n### LibZip\n\nThis is a binding to the C library [`libzip`][libzip]. There is always a\ncertain kind of trouble with bindings. For example, you need to ensure that\nthe target library is installed and its version is compatible with the\nversion of your binding.\n\nIt's not that bad with libraries that do not break their API for years, but\nit's not the case with `libzip`. As the maintainer of `LibZip` puts it:\n\n\u003e libzip 0.10, 0.11, and 1.0 are not binary compatible. If your C library is\n\u003e 0.11.x, then you should use LibZip 0.11. If your C library is 1.0, then\n\u003e you should use LibZip master branch (not yet released to Hackage).\n\nNow, on my machine I have the version 1.0. To put the package on Stackage we\nhad to use the version 0.10, because Stackage uses Ubuntu to build packages\nand libraries on Ubuntu are always ancient. This means that I cannot use the\nversion of the library from Stackage, and I don't yet know what will be on\nthe server.\n\nAfter much frustration, I decided to avoid using `LibZip`. After all, this\nis not a project that shouldn't be done completely in Haskell. By rewriting\nthis in Haskell, I also can make it safer to use.\n\n### zip-conduit\n\nThis one uses the right approach: leverage a good streaming library\n(`conduit`) for memory-efficient processing. The library is however not\nfeature-rich and has certain problems (including the programming style, it\nuses `error` if an entry is missing in the archive, among other things),\nsome of them are reported on its issue tracker. It also does not appear to\nbe maintained (the last sign of activity was on December 23, 2014).\n\n## Features\n\nThe library supports all features specified in the modern .ZIP specification\nexcept for encryption and multi-disk archives. See more about this below.\n\nFor reference, here is a [copy of the specification][specification].\n\n### Compression methods\n\n`zip` supports the following compression methods:\n\n* Store (no compression, just store files “as is”)\n* [DEFLATE]\n* [Bzip2]\n* [Zstandard]\n\nThe best way to add a new compression method to the library is to write a\nconduit that will do the compression and publish it as a library. `zip` can\nthen depend on it and add it to the list of supported compression methods.\nThe current list of compression methods reflects what is available on\nHackage at the moment.\n\n### Encryption\n\nEncryption is currently not supported. Encryption system described in the\n.ZIP specification is known to be seriously flawed, so it's probably not the\nbest way to protect your data anyway. The encryption method seems to be a\nproprietary technology of PKWARE (at least that's what stated about it in\nthe .ZIP specification), so to hell with it.\n\n### Sources of file data\n\nThe following sources are supported:\n\n* *File name.* This is an efficient method to perform compression or\n  decompression. You specify where to get data or where to save it and the\n  rest is handled by the library.\n* *Conduit source or sink.*\n* *ByteString.* Use it only with small data.\n* *Copy file from another archive.* An efficient operation, file is copied\n  “as is”—no re-compression is performed.\n\n### ZIP64\n\nWhen necessary, the `ZIP64` extension is automatically used. It's necessary\nwhen:\n\n* The total size of the archive is greater than 4 GB.\n* The size of a single compressed/uncompressed file in the archive is\n  greater than 4 GB.\n* There are more than 65535 entries in the archive.\n\nThe library is particularly well suited for processing large files. For\nexample, I've been able to create 6.5 GB archive with reasonable speed and\nwithout significant memory consumption.\n\n### Filenames\n\nThe library has an API that makes it impossible to create archive with\nnon-portable or invalid file names in it.\n\nAs of .ZIP specification 6.3.2, files with Unicode symbols in their names\ncan be stored in Zip archives. The library supports mechanisms for this and\nuses them automatically when needed. Besides UTF-8, CP437 is also supported\nas per the specification.\n\n### Meta-information about files\n\nThe library allows us to attach comments to the entire archive or individual\nfiles, and also gives its user full control over extra fields that are\nwritten to file headers, so the user can store arbitrary information about\nfiles in the archive.\n\n## Quick start\n\nThe module `Codec.Archive.Zip` provides everything you may need to\nmanipulate Zip archives. There are three things that should be clarified\nright away to avoid confusion.\n\nFirst, we use the `EntrySelector` type that can be obtained from relative\n`FilePath`s (paths to directories are not allowed). This method may seem\nawkward at first, but it will protect you from the problems with portability\nwhen your archive is unpacked on a different platform.\n\nSecond, there is no way to add directories, or to be precise, *empty\ndirectories* to your archive. This approach is used in Git and I find it\nsane.\n\nFinally, the third feature of the library is that it does not modify the\narchive instantly, because doing so on every manipulation would often be\ninefficient. Instead, we maintain a collection of pending actions that can\nbe turned into an optimized procedure that efficiently modifies the archive\nin one pass. Normally, this should be of no concern to you, because all\nactions are performed automatically when you leave the `ZipArchive` monad.\nIf, however, you ever need to force an update, the `commit` function is your\nfriend.\n\nLet's take a look at some examples that show how to accomplish most common\ntasks.\n\nTo get full information about archive entries, use `getEntries`:\n\n```haskell\nλ\u003e withArchive archivePath (M.keys \u003c$\u003e getEntries)\n```\n\nThis will return a list of all entries in the archive at `archivePath`. It's\npossible to extract contents of an entry as a strict `ByteString`:\n\n```haskell\nλ\u003e withArchive archivePath (getEntry entrySelector)\n```\n\n…to stream them to a given sink:\n\n```haskell\nλ\u003e withArchive archivePath (sourceEntry entrySelector mySink)\n```\n\n…to save a specific entry to a file:\n\n```haskell\nλ\u003e withArchive archivePath (saveEntry entrySelector pathToFile)\n```\n\n…and finally just unpack the entire archive into a directory:\n\n```haskell\nλ\u003e withArchive archivePath (unpackInto destDir)\n```\n\nSee also `getArchiveComment` and `getArchiveDescription`.\n\nModifying is also easy. When you want to create a new archive use\n`createArchive`, otherwise `withArchive` will do. To add an entry from\n`ByteString`:\n\n```haskell\nλ\u003e createArchive archivePath (addEntry Store \"Hello, World!\" entrySelector)\n```\n\nYou can stream from `Source` as well:\n\n```haskell\nλ\u003e createArchive archivePath (sinkEntry Deflate source entrySelector)\n```\n\nTo add contents from a file, use `loadEntry`:\n\n```haskell\nλ\u003e let toSelector = const (mkEntrySelector \"my-entry.txt\")\nλ\u003e createArchive archivePath (loadEntry BZip2 toSelector myFilePath)\n```\n\nFinally, you can copy an entry from another archive without re-compression\n(unless you use `recompress`, see below):\n\n```haskell\nλ\u003e createArchive archivePath (copyEntry srcArchivePath selector selector)\n```\n\nIt's often desirable to just pack a directory:\n\n```haskell\nλ\u003e createArchive archivePath (packDirRecur Deflate mkEntrySelector dir)\n```\n\nIt's also possible to:\n\n* rename an entry with `renameEntry`\n* delete an entry with `deleteEntry`\n* change compression method with `recompress`\n* change comment associated with an entry with `setEntryComment`\n* delete comment with `deleteEntryComment`\n* set modification time with `setModTime`\n* manipulate extra fields with `addExtraField` and `deleteExtraField`\n* check if entry is intact with `checkEntry`\n* undo changes with `undoEntryCanges`, `undoArchiveChanges`, and `undoAll`\n* force changes to be written to file system with `commit`\n\nThis should cover all your needs. Feel free to open an issue if you're\nmissing something.\n\n## Contribution\n\nYou can contact the maintainer via [the issue\ntracker](https://github.com/mrkkrp/zip/issues).\n\nPull requests are welcome.\n\n## License\n\nCopyright © 2016–present Mark Karpov\n\nDistributed under BSD 3 clause license.\n\n[libzip]: https://en.wikipedia.org/wiki/Libzip\n[specification]: https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.3.TXT\n[DEFLATE]: https://en.wikipedia.org/wiki/DEFLATE\n[Bzip2]: https://en.wikipedia.org/wiki/Bzip2\n[Zstandard]: https://en.wikipedia.org/wiki/Zstandard\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrkkrp%2Fzip","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmrkkrp%2Fzip","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrkkrp%2Fzip/lists"}