Gzip压缩数据解压

在进行微博数据解析的过程中,遇到了gzip格式的压缩数据,要从这些数据中得到微博信息就首先需要对gzip数据进行解压。

这里采用的解压工具是zlib(http://www.zlib.net/),关于zlib的技术细节参考http://www.zlib.net/zlib_tech.html


重组后的微博TCP会话中的压缩数据:

从上图可以看出,gzip数据的开始是从两个换行“\r\n”开始的,即从“65c”这行数据后边开始的,是以“0”这一行结束的。”65c“表示的是其下面那段压缩数据的长度;上图中gzip数据仅分了65c这么长的一段,而某次压缩的数据可能分多个段,那么每段数据均以类似"65c"这么一个表示长度的值开始,后跟本段压缩数据。


下面这个函数是将一段一段的压缩gzip数据进行合并:

//第一个参数是待处理的http数据,第二个参数是数据的长度
//该函数处理重组后的http数据中的gzip压缩数据:
//gzip数据是以一个或多个chunked的形式存在的,该函数将提取,合并并解压出所有chunk的数据(解压
//出的gzip数据是是json格式的,函数返回的解压内容,在后续处理中会提取出json的“html”字段,进一步得到微博id)
void ProcessGzipData(char *source, int len, char *decompression) {
	char result_gzip[65530];
	char pattern[] = "\r\n\r\n";
	int begin_pos = KmpSearch(source, len, pattern) + strlen(pattern);
	if (begin_pos == -1)   
		return;
	int offset = 0;
	int gzip_len = 0;
	while (memcmp(source + begin_pos + offset, "0\r\n", 3) != 0) {
		char pattern2[] = "\r\n";
		int len1 = KmpSearch(source + begin_pos + offset, len - begin_pos - offset, pattern2);
		if (len1 == -1)   //压缩数据出错,返回
			break;
		char temp1[10] = {'\0'};
		memcpy(temp1, source + begin_pos + offset, len1);
		offset += (len1 + strlen("\r\n"));
		int len2 = KmpSearch(source + begin_pos + offset, len - begin_pos - offset, pattern2);
		memmove(result_gzip + gzip_len, source + begin_pos + offset, len2);
		gzip_len += len2;
		offset += (len2 + strlen("\r\n"));
	}
/*
	fstream myfile("/home/yang/test/zlib.file", fstream::in | fstream::out | fstream::app);
	if (!myfile)
		cout << "open file error" << endl;
	
	int i;
	cout << "gzip len: " << gzip_len << endl;
	for (i = 0; i < gzip_len; ++i)
		myfile << result_gzip[i];
	myfile.close();
*/
	DecompressGzip(result_gzip, gzip_len + 1000,  decompression);  //调用下面的函数对合并的gzip数据解压
}


解压gzip数据的代码如下:

//该函数解压gzip数据
//参数:source是指向待解压数据的指针;len是待解压数据的长度;destination用于存放解压后的数据
int DecompressGzip(char *source, int len, char *destination) {
	int result, have;
	int offset = 0;
	z_stream d_stream;

	unsigned char compression[SEGMENT_SIZE] = {'\0'}, decompression[SEGMENT_SIZE] = {'\0'};
	memcpy(compression, (Byte*)source, len);
	unsigned int compression_len = len, decompression_len = SEGMENT_SIZE * 4;
	strcpy((char*)decompression, "garbage");
	d_stream.zalloc = Z_NULL;
	d_stream.zfree = Z_NULL;
	d_stream.opaque = Z_NULL;
	d_stream.next_in = Z_NULL;
	d_stream.avail_in = 0;

	result = inflateInit2(&d_stream, 47);
	if (result != Z_OK) {
		printf("inflateInit2 error: %d\n", result);	
		return result;
	}

	d_stream.next_in = compression;
	d_stream.avail_in = compression_len;

	do {
		d_stream.next_out = decompression;
		d_stream.avail_out = SEGMENT_SIZE;
		result = inflate(&d_stream, Z_NO_FLUSH);

		assert(result != Z_STREAM_ERROR);

		switch (result) {
		case Z_NEED_DICT:
			result = Z_DATA_ERROR;
		case Z_DATA_ERROR:
		case Z_MEM_ERROR:
			(void)inflateEnd(&d_stream);
			return result;	
		}
		have = SEGMENT_SIZE - d_stream.avail_out;
		memcpy(destination + offset, decompression, have);

		offset += have;
	} while (d_stream.avail_out == 0);

	inflateEnd(&d_stream);
	memcpy(destination + offset, "\0", 1);
	return result;
}

参考:

zlib and gzip

获取http的gzip内容并解压相关问题

gzip 使用zlib解压http中gzip内容

zlib库解压http报文中的gzip数据

RFC:GZIP file format specification version 4.3


版权声明:本文为u013074465原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。