1. 程式人生 > >Gzip壓縮資料解壓

Gzip壓縮資料解壓

在進行微博資料解析的過程中,遇到了gzip格式的壓縮資料,要從這些資料中得到微博資訊就首先需要對gzip資料進行解壓。

重組後的微博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;
}

參考: