Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
Z

zip

> 编程语言
Open source

A portable, simple zip library written in C

1.6K stars0 likes2 views
WebsiteGitHub

About

A portable, simple zip library written in C

A portable (OSX/Linux/Windows/Android/iOS), simple zip library written in C

This is done by hacking awesome miniz library and layering functions on top of the miniz v3.1.2 API.

The Idea

... Some day, I was looking for zip library written in C for my project, but I could not find anything simple enough and lightweight. Everything what I tried required 'crazy mental gymnastics' to integrate or had some limitations or was too heavy. I hate frameworks, factories and adding new dependencies. If I must to install all those dependencies and link new library, I'm getting almost sick. I wanted something powerful and small enough, so I could add just a few files and compile them into my project. And finally I found miniz. Miniz is a lossless, high performance data compression library in a single source file. I only needed simple interface to append buffers or files to the current zip-entry. Thanks to this feature I'm able to merge many files/buffers and compress them on-the-fly.

It was the reason, why I decided to write zip module on top of the miniz. It required a little bit hacking and wrapping some functions, but I kept simplicity. So, you can grab these 3 files and compile them into your project. I hope that interface is also extremely simple, so you will not have any problems to understand it.

Examples

  • Create a new zip archive with default compression level.
c
struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
{
    zip_entry_open(zip, "foo-1.txt");
    {
        const char *buf = "Some data here...\0";
        zip_entry_write(zip, buf, strlen(buf));
    }
    zip_entry_close(zip);

    zip_entry_open(zip, "foo-2.txt");
    {
        // merge 3 files into one entry and compress them on-the-fly.
        zip_entry_fwrite(zip, "foo-2.1.txt");
        zip_entry_fwrite(zip, "foo-2.2.txt");
        zip_entry_fwrite(zip, "foo-2.3.txt");
    }
    zip_entry_close(zip);
}
zip_close(zip);
  • Append to the existing zip archive.
c
struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
{
    zip_entry_open(zip, "foo-3.txt");
    {
        const char *buf = "Append some data here...\0";
        zip_entry_write(zip, buf, strlen(buf));
    }
    zip_entry_close(zip);
}
zip_close(zip);
  • Extract a zip archive into a folder.
c
int on_extract_entry(const char *filename, void *arg) {
    static int i = 0;
    int n = *(int *)arg;
    printf("Extracted: %s (%d of %d)\n", filename, ++i, n);

    return 0;
}

// From "foo.zip" on disk
int arg = 2;
zip_extract("foo.zip", "/tmp", on_extract_entry, &arg);

// Or from memory
arg = 2;
zip_stream_extract(zipstream, zipstreamsize, "/tmp", on_extract_entry, &arg);
  • Extract a zip entry into memory.
c
void *buf = NULL;
size_t bufsize;

struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
    zip_entry_open(zip, "foo-1.txt");
    {
        zip_entry_read(zip, &buf, &bufsize);
    }
    zip_entry_close(zip);
}
zip_close(zip);

free(buf);
  • Extract a zip entry into memory (no internal allocation).
c
unsigned char *buf;
size_t bufsize;

struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
    zip_entry_open(zip, "foo-1.txt");
    {
        bufsize = zip_entry_size(zip);
        buf = calloc(sizeof(unsigned char), bufsize);

        zip_entry_noallocread(zip, (void *)buf, bufsize);
    }
    zip_entry_close(zip);
}
zip_close(zip);

free(buf);
  • Extract a zip entry into memory using callback.
…
  • Extract a zip entry into a file.
c
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
    zip_entry_open(zip, "foo-2.txt");
    {
        zip_entry_fread(zip, "foo-2.txt");
    }
    zip_entry_close(zip);
}
zip_close(zip);
  • Create a new zip archive in memory (stream API).
c
char *outbuf = NULL;
size_t outbufsize = 0;

const char *inbuf = "Append some data here...\0";
struct zip_t *zip = zip_stream_open(NULL, 0, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
{
    zip_entry_open(zip, "foo-1.txt");
    {
        zip_entry_write(zip, inbuf, strlen(inbuf));
    }
    zip_entry_close(zip);

    /* copy compressed stream into outbuf */
    zip_stream_copy(zip, (void **)&outbuf, &outbufsize);
}
zip_stream_close(zip);

free(outbuf);
  • Extract a zip entry into memory (stream API).
c
char *buf = NULL;
size_t bufsize = 0;

struct zip_t *zip = zip_stream_open(zipstream, zipstreamsize, 0, 'r');
{
    zip_entry_open(zip, "foo-1.txt");
    {
        zip_entry_read(zip, (void **)&buf, &bufsize);
    }
    zip_entry_close(zip);
}
zip_stream_close(zip);

free(buf);
  • Extract a partial zip entry

Reads up to size bytes of the entry starting at offset into a caller-owned buffer (no internal allocation). size is clamped to the bytes remaining after offset, so it is safe to pass a size larger than what is left in the entry. The call returns the number of bytes actually written, or a negative error code (for example when offset is past the end of the entry).

c
unsigned char buf[16];
size_t bufsize = sizeof(buf);

struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
    zip_entry_open(zip, "foo-1.txt");
    {
        size_t offset = 4;
        ssize_t nread = zip_entry_noallocreadwithoffset(zip, offset, bufsize, (void *)buf);
        if (nread < 0) {
            // offset out of range or read error
        }
    }

    zip_entry_close(zip);
}
zip_close(zip);
  • List of all zip entries
c
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
int i, n = zip_entries_total(zip);
for (i = 0; i < n; ++i) {
    zip_entry_openbyindex(zip, i);
    {
        const char *name = zip_entry_name(zip);
        int isdir = zip_entry_isdir(zip);
        int issymlink = zip_entry_issymlink(zip);
        unsigned long long size = zip_entry_size(zip);
        unsigned int crc32 = zip_entry_crc32(zip);
    }
    zip_entry_close(zip);
}
zip_close(zip);
  • Compress folder (recursively)
…
  • Delete zip archive entries.
c
char *entries[] = {"unused.txt", "remove.ini", "delete.me"};
// size_t indices[] = {0, 1, 2};

struct zip_t *zip = zip_open("foo.zip", 0, 'd');
{
    zip_entries_delete(zip, entries, 3);

    // you can also delete by index, instead of by name
    // zip_entries_deletebyindex(zip, indices, 3);
}
zip_close(zip);
  • Create a password-protected zip archive (Traditional PKWARE Encryption).
c
struct zip_t *zip = zip_open_with_password("secret.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'w', "password");
{
    zip_entry_open(zip, "secret-1.txt");
    {
        const char *buf = "Classified data...\0";
        zip_entry_write(zip, buf, strlen(buf));
    }
    zip_entry_close(zip);
}
zip_close(zip);
  • Extract a password-protected zip archive.
c
void *buf = NULL;
size_t bufsize;

struct zip_t *zip = zip_open_with_password("secret.zip", 0, 'r', "password");
{
    zip_entry_open(zip, "secret-1.txt");
    {
        zip_entry_read(zip, &buf, &bufsize);
    }
    zip_entry_close(zip);
}
zip_close(zip);

free(buf);
  • Password-protected archive in memory (stream API).
…
  • Delete entries from a password-protected archive.
c
char *entries[] = {"obsolete.txt", "remove-me.dat"};

struct zip_t *zip = zip_open_with_password("secret.zip", 0, 'd', "password");
{
    zip_entries_delete(zip, entries, 2);
}
zip_close(zip);

Bindings

Compile zip library as a dynamic library.

bash
$ mkdir build
$ cd build
$ cmake -DBUILD_SHARED_LIBS=true ..
$ cmake --build .

Go (cgo)

Third party binding: kuba--/c-go-zip

go
package main

/*
#cgo CFLAGS: -I../src
#cgo LDFLAGS: -L. -lzip
#include <zip.h>
*/
import "C"
import "unsafe"

func main() {
    path := C.CString("/tmp/go.zip")
    zip := C.zip_open(path, 6, 'w')

    entryname := C.CString("test")
    C.zip_entry_open(zip, entryname)

    content := "test content"
    buf := unsafe.Pointer(C.CString(content))
    bufsize := C.size_t(len(content))
    C.zip_entry_write(zip, buf, bufsize)

    C.zip_entry_close(zip)

    C.zip_close(zip)
}

Rust (ffi)

…

Ruby (ffi)

Install ffi gem.

bash
$ gem install ffi

Bind in your module.

…

Python (cffi)

Install cffi package

bash
$ pip install cffi

Bind in your package.

…

Never (ffi)

…

Ring

The language comes with RingZip based on this library

ring
load "ziplib.ring"

new Zip {
    setFileName("myfile.zip")
    open("w")
    newEntry() {
        open("test.c")
        writefile("test.c")
        close()
    }
    close()
}

Zig

bash
$ zig build-exe main.zig -lc -lzip
zig
const c = @cImport({
    @cInclude("zip.h");
});

pub fn main() void {
    var zip = c.zip_open("/tmp/zig.zip", 6, 'w');
    defer c.zip_close(zip);

    _ = c.zip_entry_open(zip, "test");
    defer _ = c.zip_entry_close(zip);

    const content = "test content";
    _ = c.zip_entry_write(zip, content, content.len);
}

Odin

Third party binding: thechampagne/zip-odin

…

Nim

Third party binding: thechampagne/nimzip

bash
$ nim c --passL:-lzip main.nim
nim
proc zip_open(zipname: cstring, level: cint, mode: char): pointer {.importc.}
proc zip_close(zip: pointer) {.importc.}
proc zip_entry_open(zip: pointer, entryname: cstring): cint {.importc.}
proc zip_entry_close(zip: pointer): cint {.importc.}
proc zip_entry_write(zip: pointer, buf: pointer, bufsize: csize_t): cint {.importc.}

when isMainModule:
  var zip = zip_open("/tmp/nim.zip", 6, 'w')

  discard zip_entry_open(zip, "test")

  let content: cstring = "test content";
  discard zip_entry_write(zip, content, csize_t(len(content)))

  discard zip_entry_close(zip)
  zip_close(zip)

D

Third party binding: thechampagne/zip-d

bash
$ dmd -L-lzip main.d
d
extern(C) void* zip_open(const(char)* zipname, int level, char mode);
extern(C) void zip_close(void* zip);
extern(C) int zip_entry_open(void* zip, const(char)* entryname);
extern(C) int zip_entry_close(void* zip);
extern(C) int zip_entry_write(void* zip, const(void)* buf, size_t bufsize);

void main()
{
  void* zip = zip_open("/tmp/d.zip", 6, 'w');
  scope(exit) zip_close(zip);

  zip_entry_open(zip, "test");
  scope(exit) zip_entry_close(zip);

  string content = "test content";
  zip_entry_write(zip, content.ptr, content.length);
}

Pascal

Third party binding: thechampagne/zip-pascal

…

lua

Third party binding: gynt/luamemzip

lua
-- Loads the luamemzip.dll
zip = require("luamemzip")

content = "Test content"

z = zip.zip_stream_open(nil, 6, 'w')

zip.zip_entry_open(z, "test.content")
zip.zip_entry_write(z, content)
zip.zip_entry_close(z)

data = zip.zip_stream_copy(z)

zip.zip_close(z)

Optional features

The library supports three compile-time flags to strip unused functionality and reduce binary size. All features are enabled by default -- if you just drop the source files into your project and compile, everything works as before.

Flag Default Effect when disabled (=0)
ZIP_ENABLE_DEFLATE 1 Removes compression / archive-writing APIs (zip_entry_write, zip_entry_fwrite, zip_create, zip_entries_delete, …). Write and append modes ('w', 'a', 'd') return an error.
ZIP_ENABLE_INFLATE 1 Removes decompression / extraction APIs (zip_entry_read, zip_entry_fread, `zip_entry_ex

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Cccompressionhackingminiz

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言