开发背景
在android ndk环境下使用libcurl发起http请求,不巧的是,当前使用的libcurl编译时没有包含zlib模块,导致无法解压gzip数据。
考虑到ndk自带zlib包,可以使用zlib手动解压gzip数据,避免再次编译libcurl。
源码实现
(1)使用zlib解压gz数据
int gzDecompress(const char *src, int srcLen, const char *dst, int* dstLen){
z_stream strm;
strm.zalloc = NULL;
strm.zfree = NULL;
strm.opaque = NULL;
strm.avail_in = srcLen;
strm.avail_out = *dstLen;
strm.next_in = (Bytef *)src;
strm.next_out = (Bytef *)dst;
int err = -1;
err = inflateInit2(&strm, MAX_WBITS + 16); /*zlib解压gz数据*/
if (err == Z_OK){
err = inflate(&strm, Z_FINISH);
if (err == Z_STREAM_END){
*dstLen = strm.total_out; /* 解压成功 */
} else{
inflateEnd(&strm); /* 解压失败 */
return err;
}
} else{
inflateEnd(&strm); /* 解压初始化失败 */
return err;
}
inflateEnd(&strm);
return err;
}
bool tryDecompressGzip(const char *src, int srcLen, string& resData) {
bool success = false;
for (int i = 1; i <= 3; i++) {
int buffSize = (srcLen * 10 + 3000)*i; /* 解压缓冲区大小,自动扩容 */
char* buffData = (char*)malloc(buffSize);
memset(buffData, 0, buffSize);
int ret = gzDecompress(src, srcLen, buffData, &buffSize);
if (Z_STREAM_END == ret) { /* 解压成功 */
resData = buffData;
free(buffData);
success = true;
break;
}
else {
free(buffData); /* 解压失败,扩容后再次尝试 */
}
}
return success;
}
需要注意的地方是buffSize的大小,假设未解压的大小为1000,解压后可能为3000,也可能为10000。 考虑到gzip压缩后的大小可能为原有大小的70%~10%,分配10倍空间应该是足够的。
(2)libcurl请求header加上gzip标识
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip");
(3)libcurl设置读取响应header的回调函数
int contentEncodingGzip = 0; /* 响应头是否有gzip */
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, OnHeaderData);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &contentEncodingGzip);
(4)添加读取响应header的回调函数
/*libcurl收到http header的回调*/
static size_t OnHeaderData(void *ptr, size_t size, size_t nmemb, void *stream)
{
if (strncmp((char *)ptr, "Content-Encoding: gzip", strlen("Content-Encoding: gzip")) == 0) {
*((int*)stream) = 1;
}
return size * nmemb;
}
检测响应header是否包含 "Content-Encoding: gzip"
(5)若返回gzip数据,执行解压
if (1 == contentEncodingGzip) { /* 响应头有gzip标识,解压响应body */
string decompress;
if (tryDecompressGzip(strResponse.data(), strResponse.size(), decompress)) {
strResponse = decompress;
}
}
其中 strResponse为解压前的数据。
至此可以实现用zlib解压gzip数据。