标签:
官方手册 :
http://www.zlib.net/manual.html
参考连接 :
http://blog.csdn.net/zhoudaxia/article/details/8039519
http://www.oschina.net/code/snippet_65636_22542
http://blog.163.com/bh_binghu/blog/static/94553512011626102054571/
请多多支持以上作者...
另外,推荐下载源码同步学习.
研究了好久的zlib,今天终于可以拿得出手了.这里分享一下使用的经验.
z_stream : 压缩算法,压缩程度以及输入输出buffer和长度等都保存在这里,可以理解为压缩上下文.
deflateInit : 参数比较少,里面的实现其实是调用的deflateInit2
deflateInit2 : 压缩初始化的基础函数,有很多参数,下面会重点介绍
deflate : 压缩函数.
deflateEnd : 压缩完成以后,释放空间,但是注意,仅仅是释放deflateInit中申请的空间,自己申请的空间还是需要自己释放.
inflateInit : 解压初始化函数,内部调用的inflateInit2.
inflateInit2 : 解压初始化的基础函数.后面重点介绍.
infalte : 解压函数
inflateEnd : 同deflateEnd作用类似.
compress : 全部附加选项默认压缩,内部调用compress2
compress2 : 带level的压缩方式.
uncompress : 解压缩.
deflateInit2 :
函数原型为 : int ZEXPORT deflateInit2(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)
z_stream : 这个是压缩上下文,你在调用deflateInit2之前首先要做的就是初始化这个结构体,很简单,个里面的三个回调赋值就行了.连官方给出的例子也是置成NULL.所以这里直接这样初始化,strm.zalloc = NULL; strm.zfree = NULL; strm.opaque = NULL.
level : 这个参数目前可选值有四个(好吧,在我看来,有两个没啥用嘛),如下:
Z_NO_COMPRESSION : 不压缩
Z_BEST_SPEED : 速度优先,可以理解为最低限度的压缩.
Z_BEST_COMPRESSION : 压缩优先,但是速度会有些慢.
Z_DEFAULT_COMPRESSION : 没仔细看源码.compress里面用的就是这个选项.
method : 就压缩嘛,所以选项就只有Z_DEFLATED
windowBits : 这个选项可就有点复杂了,也很重要,取值与压缩方式如下:
* -(15 ~ 8) : 纯deflate压缩
* 15 ~ 8 : 带zlib头和尾
* > 16 : 带gzip头和尾
所以,无论是zlib的压缩还是gzip的压缩,再或者是纯deflate流,zlib都能搞定.验证方式 :
1. zlib的话可以使用命令 cat 压缩文件名 | zlib-deflate -uncompress验证(家里的电脑没有这个命令,忘记什么包里面的了.)
2. 如果是纯deflate的压缩数据,只能字节写解压的代码.注意调用inflateInit2的时候这个值要对应.
3. 如果是gzip的方式,就直接用gunzip验证.
memLevel : 目前只有一个选项,MAX_MEM_LEVEL,无非是运行过程中对内存使用的限制.
strategy : 直接给默认就行Z_DEFAULT_STRATEGY.
deflate :
函数原型 : ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
strm : 这个参数就是上面调用了deflateInit2初始化过后的变量.调用deflate之前,需要为它指定四个成员.
strm.next_in = 你的待压缩数据
strm.next_out = 压缩以后数据存储的buffer
strm.avail_in = 待压缩数据的长度
strm.avail_out = 压缩数据存储buffer的长度.
这个next_out 没有固定的长度,并不是说因为是压缩,压缩以后的数据一定比原来的短,所以就可以少给,一般建议起码给到next_in的长度.
flush : 没有用过别的选项,
Z_NO_FLUSH : 如果是一个大文件,没有办法一次加载进内存,那么这个选项就派上用场了.它告诉zlib我还会再给你数据的.
这个参数对应的deflate的返回值是Z_OK.
Z_FINISH : 这个标记才是告诉zlib,我没有数据了,这次是最后一个.
这个参数对应的deflate的返回值是Z_STREAM_END
deflateEnd :
上面的描述已经足够清楚,它没有更多的作用了.
compress2 :
函数原型 : ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level));
如果你看了它的内部实现,就会知道,这其实就是上面三个函数的封装.供使用者偷懒用的,谁不喜欢一步到位呢.不过要明确它的缺点 :
a) 它的确很方便,但是却限制了所有的可能性,因为它只能压缩一次,也就是说内部直接调用的Z_FINISH
b) 不要觉得保存的是压缩的数据就给小buffer,有一篇博客给出的值是输入buffer的四倍.这个亏我是吃过的,前几天用这个函数,给了输入buffer的大小,我觉得足够了.但是始终返回Z_BUF_ERROR.后来看到那篇博客才恍然大悟.
inflateInit2 : 初始化
函数原型 : inflateInit2(z_streamp strm, int windowBits)
strm : 和deflate一样,初始化三个回调以后即可.有的参考文档说还需要初始化第四个选项,具体记不清哪个了.不过我试过以后发现貌似不用.
windownBits : 含义和deflateInit2一样,而且一定要对应起来.
inflate : 解压
函数原型 : ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
strm : 四个参数.
strm.next_in = 你的待解压数据
strm.next_out = 解压以后数据存储的buffer
strm.avail_in = 待解压数据的长度
strm.avail_out = 解压数据存储buffer的长度.
flush : 和deflate一样,如果是Z_NO_FLUSH说明还有数据没有解压,如果是Z_FINISH说明这是最后一包待解压数据.
inflateEnd : 释放上面两步骤里面申请的资源.
uncompress :
函数原型 : ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen));
dest : 解压以后的数据存在这里
destLen : dest 大小
source : 待解压数据buffer
sourceLen : 待解压数据长度
其实这个函数就是简单封装了inflateInit, inflate, inflateEnd.同样,这个函数只适合单独解压场景,不适合需要多次传入的场景.
现在看来,除了deflate和inflate成功会有两个返回值以外,其他的函数返回值成功的话都是Z_OK.
如果 flush == Z_NO_FLUSH, 这两个函数返回值是Z_OK
如果flush == Z_FINISH, 这两个函数的返回值是Z_STREAM_END
一定要区分.
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <sys/stat.h> #include <fcntl.h> #include <zlib.h> #include <time.h> #include <errno.h> #include <assert.h> #define READ_LEN 8192 // gzip头 #define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */ // 目前只有这种压缩方式 #define DEFLATED 8 // 标记压缩为快速还是高质量 #define FAST 4 #define SLOW 2 // gzip格式 // (u16)MAGIC + (u8)DEFLATE + (u8)flag + (u32)time + (u8)deflate_flag + (u8)OS_CODE int main(int argc, char **argv) { if (argc < 3) { printf("usage : %s src_file dst_file\n", argv[1]); return -1; } int ret = 0; int rfd = 0; uint64_t read_len = 0; char *read_buf = NULL; int wfd = 0; uint64_t write_len = 0; char *write_buf = NULL; rfd = open(argv[1], O_RDONLY); if (-1 == rfd) { perror("\n"); return -1; } wfd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666); if (wfd < 0) { perror("\n"); return -1; } read_buf = (char *)malloc(READ_LEN); if (NULL == read_buf) { return -1; } write_len = READ_LEN * 4; write_buf = (char *)malloc(write_len); if (NULL == write_buf) { return -1; } uint8_t *ptr = (uint8_t*)write_buf; z_stream stream; stream.zalloc = NULL; stream.zfree = NULL; stream.opaque = NULL; deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY); AGAIN: while ((read_len = read(rfd, read_buf, READ_LEN)) >= 0) { int flush = Z_NO_FLUSH; if (read_len == 0) { flush = Z_FINISH; } stream.next_in = (uint8_t*)read_buf; stream.avail_in = read_len; do { write_len = 4 * READ_LEN; stream.next_out = (uint8_t*)write_buf; stream.avail_out = write_len; ret = deflate(&stream, flush); if (flush == Z_NO_FLUSH && ret != Z_OK) { goto ERROR; } if (flush == Z_FINISH && ret != Z_STREAM_END) { goto ERROR; } int avail = 4 * READ_LEN - stream.avail_out; write(wfd, write_buf, avail); }while (stream.avail_out == 0); // 如果avail_out不够用了,说明输入的buffer还有没有压缩的部分,那么要继续压缩. // 如果输入长度不是0,说明压缩不完整,可以直接退出. assert(stream.avail_in == 0); if (read_len == 0) { break; } } if (read_len < 0) { if (errno == EINTR) { goto AGAIN; } } return 0; ERROR: deflateEnd(&stream); close(rfd); close(wfd); return -1; }这段代码,我使用的是gzip的压缩方式,所以如果要解压,不需要在单独写一份代码了.只要gunzip命令就可以验证程序是否正常.
后记 :
关于三种压缩格式的区分.
deflate : 说他是压缩格式有点不确切,其实它就是压缩的核心算法.
zlib : zlib header + deflate + zlib tailer
gzip : gzip header + deflate + gzip tailer
另外我封装了一套单buffer的压缩和解压函数.也放在这里,有需要的同学可以参考一下.
compress.h
#ifndef MYZIP_H #include <unistd.h> #include <stdint.h> #include <zlib.h> #ifdef __cplusplus extern "C" { #endif typedef z_streamp PZSTREAM; typedef z_stream ZSTREAM; /* =========================================================================== * 指定压缩(解压)类型 * 官方文档提到 : * -(15 ~ 8) : raw deflate压缩 * 15 ~ 8 : 带zlib头和尾 * > 16 : 带gzip头和尾 */ typedef enum { E_ZIP_RAW = -MAX_WBITS, E_ZIP_ZLIB = MAX_WBITS, E_ZIP_GZIP = MAX_WBITS + 16 } zip_type; // 压缩回调函数 typedef void (*flate_cb)(void *buf, int len); /****************************************************************************** ** 执行deflate压缩 ** NOTE : 1. 这组函数仅仅适用于小buffer 2. 在调用deflate_beg之前,需要自己初始化strm 3. 如果deflating失败,要执行deflate_end释放资源 4. 明确一点,deflate_end只做了stream内部资源释放工作,如果stream是动态分配的,需要自己free ******************************************************************************/ int deflate_beg(PZSTREAM strm, zip_type type); int deflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *out_len); int deflate_end(PZSTREAM strm); /****************************************************************************** ** 执行inflate解压 ** NOTE : 1. 这组函数仅仅适用于小buffer 2. 在调用inflate_beg之前,需要自己初始化strm 3. 如果inflating失败,要执行inflate_end释放资源 4. 明确一点,inflate_end只做了stream内部资源释放工作,如果stream是动态分配的,需要自己free ******************************************************************************/ int inflate_beg(PZSTREAM strm, zip_type type); int inflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *outlen); int inflate_end(PZSTREAM strm); #ifdef __cplusplus } #endif #endif
compress.c
#include <unistd.h> #include "compress.h" /// strm需要在传入之前自己初始化 int deflate_beg(PZSTREAM strm, zip_type type) { int ret = 0; int window_bits; if (NULL == strm) { goto ERROR; } strm->zalloc = Z_NULL; strm->zfree = Z_NULL; strm->opaque = Z_NULL; strm->next_in = Z_NULL; strm->avail_in = 0; window_bits = (int)type; ret = deflateInit2(strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY); if (ret != Z_OK) { goto ERROR; } return 0; ERROR: return -1; } int deflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *out_len) { int ret = 0; if (NULL == strm || NULL == inbuf || NULL == outbuf || NULL == out_len) { goto ERROR; } ret = deflateReset(strm); if (ret != Z_OK) { goto ERROR; } strm->next_in = inbuf; strm->avail_in = in_len; strm->next_out = outbuf; strm->avail_out = *out_len; // 此函数的应用场景都是小buffer场景,所以这里inbuf的传入都是完整需要压缩的, // 所以这里直接调用Z_FINISH ret = deflate(strm, Z_FINISH); if (ret != Z_STREAM_END) { goto ERROR; } *out_len = strm->total_out; return 0; ERROR: return -1; } int deflate_end(PZSTREAM strm) { int ret = 0; ret = deflateEnd(strm); if (ret != Z_OK) { goto ERROR; } return 0; ERROR: return -1; } int inflate_beg(PZSTREAM strm, zip_type type) { int ret = 0; int window_bits = 0; if (NULL == strm) { goto ERROR; } strm->zalloc = (alloc_func)0; strm->zfree = (free_func)0; strm->opaque = (voidpf)0; window_bits = (int)type; ret = inflateInit2(strm, window_bits); if (ret != Z_OK) { goto ERROR; } return 0; ERROR: return -1; } int inflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *outlen) { int ret = 0; if (NULL == strm || NULL == inbuf || NULL == outbuf || NULL == outlen) { goto ERROR; } ret = inflateReset(strm); if (ret != Z_OK) { goto ERROR; } strm->next_in = inbuf; strm->avail_in = in_len; strm->next_out = outbuf; strm->avail_out = *outlen; ret = inflate(strm, Z_FINISH); if (Z_STREAM_END != ret) { goto ERROR; } *outlen = strm->total_out; return 0; ERROR: return -1; } int inflate_end(PZSTREAM strm) { int ret = 0; ret = inflateEnd(strm); if (ret != Z_OK) { goto ERROR; } return 0; ERROR: return -1; }
标签:
原文地址:http://blog.csdn.net/cp3alai/article/details/51282338