使用 zlib 压缩解压文件

gzip 格式支持直接通过 gzip 命令解压或压缩

内存方式

缺点显而易见,需要很大的内存;不支持 gzip 格式文件

void read_file(const char *filename, uint8_t **buffer, size_t *size) {
    FILE *in = fopen(filename, "rb");
    fseek(in, 0, SEEK_END);
    *size = ftell(in);
    fseek(in, 0, SEEK_SET);
    *buffer = malloc(*size);
    size_t read = fread(*buffer, 1, *size, in);
    *size = read;
    fclose(in);
}

void compress_file(const char *filename_in, const char *filename_out) {
    uint8_t *buffer_in;
    size_t size;
    read_file(filename_in, &buffer_in, &size);

    FILE *out = fopen(filename_out, "wb");
    size_t buffer_size = 10240;
    if (buffer_size < size) {
        buffer_size = size;
    }
    uint8_t buffer_out[buffer_size];
    int ret = compress(buffer_out, &buffer_size, buffer_in, size);
    fwrite(buffer_out, 1, buffer_size, out);
    fclose(out);
}

void decompress_file(const char *filename_in, const char *filename_out) {
    uint8_t *buffer_in;
    size_t size;
    read_file(filename_in, &buffer_in, &size);

    FILE *out = fopen(filename_out, "wb");
    size_t buffer_size = 10240;
    if (buffer_size < size) {
        buffer_size = size;
    }
    uint8_t buffer_out[buffer_size];
    int ret = uncompress(buffer_out, &buffer_size, buffer_in, size);
    fwrite(buffer_out, 1, buffer_size, out);
    fclose(out);
}

文件流方式

必须通过文件,仅支持 gzip 格式

void compress_file(const char *filename_in, const char *filename_out) {
    FILE *in = fopen(filename_in, "rb");
    gzFile out = gzopen(filename_out, "wb");
    uint32_t buffer_len = 10240;
    uint8_t buffer[buffer_len];
    while (1) {
        size_t read = fread(buffer, 1, buffer_len, in);
        if (read == 0) {
            break;
        }
        gzwrite(out, buffer, read);
    }
    fclose(in);
    gzclose(out);
}

void decompress_file(const char *filename_in, const char *filename_out) {
    gzFile in = gzopen(filename_in, "rb");
    FILE *out = fopen(filename_out, "wb");
    uint32_t buffer_len = 10240;
    uint8_t buffer[buffer_len];
    while (1) {
        size_t read = gzread(in, buffer, buffer_len);
        if (read == 0) {
            break;
        }
        fwrite(buffer, 1, read, out);
    }
    gzclose(in);
    fclose(out);
}

内存流方式

代码更复杂,但是可配置性强,支持 zlib、gzip、deflate 三种格式

deflateInit2 参数说明

level

  • 一般写默认 Z_DEFAULT_COMPRESSION
  • 0表示不压缩
  • 1 到 9,数字越大压缩率越高,同时也会占用更多资源

method

  • 目前仅支持 Z_DEFLATED,其定义值是 8

windowBits

  • 默认值:15
  • -9 到 -15 表示纯 deflate 流
  • 9 到 15 表示是 zlib 格式
  • 大于 16 表示 gzip 格式,滑动窗口大小等于入参 -16

memLevel

  • 目前默认都是填 8

strategy

  • 一般填 Z_DEFAULT_STRATEGY

示例代码

void compress_file(const char *filename_in, const char *filename_out) {
    FILE *in = fopen(filename_in, "rb");
    FILE *out = fopen(filename_out, "wb");

    const uint32_t buffer_size = 10240;
    uint8_t buffer_in[buffer_size];
    uint8_t buffer_out[buffer_size];
    z_stream stream;
    memset(&stream, 0, sizeof(stream));
    // deflateInit(&stream, Z_DEFAULT_COMPRESSION);
    // 滑动窗口,默认是15;-9到-15表示纯deflate流;9到15表示是zlib格式;大于16表示gzip格式,滑动窗口大小等于入参-16
    // memLevel 一般填8
    int ret = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
    while (1) {
        size_t read = fread(buffer_in, 1, 10240, in);
        if (read == 0) {
            break;
        }
        stream.next_in = buffer_in;
        stream.avail_in = read;
        do {
            stream.next_out = buffer_out;
            stream.avail_out = buffer_size;
            ret = deflate(&stream, feof(in) ? Z_FINISH : Z_NO_FLUSH);
            fwrite(buffer_out, 1, buffer_size - stream.avail_out, out);
        } while (stream.avail_out == 0);
    }
    deflateEnd(&stream);
    fclose(in);
    fclose(out);
}

void decompress_file(const char *filename_in, const char *filename_out) {
    FILE *in = fopen(filename_in, "rb");
    FILE *out = fopen(filename_out, "wb");

    const uint32_t buffer_size = 10240;
    uint8_t buffer_in[buffer_size];
    uint8_t buffer_out[buffer_size];
    z_stream stream;
    memset(&stream, 0, sizeof(stream));

    int ret = inflateInit2(&stream, 15 + 16);
    while (1) {
        size_t read = fread(buffer_in, 1, 10240, in);
        if (read == 0) {
            break;
        }
        stream.next_in = buffer_in;
        stream.avail_in = read;
        do {
            stream.next_out = buffer_out;
            stream.avail_out = buffer_size;
            ret = inflate(&stream, feof(in) ? Z_FINISH : Z_NO_FLUSH);
            fwrite(buffer_out, 1, buffer_size - stream.avail_out, out);
        } while (stream.avail_out == 0);
    }
    deflateEnd(&stream);
    fclose(in);
    fclose(out);
}