開發背景
在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數據。