服务端使用gzip压缩,可以大幅度减小传输包的体积,加快客户端网络请求速度,为用户节省流量。当服务器返回的httpHeader的”Content-Encoding” 属性的值是gzip时,数据会自动被解压缩,但有时候在客户端还没拿到数据的时候,就已经被某些网关解压了,这样gzip就没有起到作用。因此可以约定其他策略,防止网关解压,例如在别的头属性中标记gzip。

如此,就需要我们自己来解压gzip数据。方法如下:添加framework库中的libbz2.1.0.dylib;

添加以下静态方法:

gzip解压:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
+ (NSData *)ungzipData:(NSData *)compressedData
{
  if ([compressedData length] == 0)
    return compressedData;
  
  unsigned full_length = [compressedData length];
  unsigned half_length = [compressedData length] / 2;
  
  NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
  BOOL done = NO;
  int status;
  
  z_stream strm;
  strm.next_in = (Bytef *)[compressedData bytes];
  strm.avail_in = [compressedData length];
  strm.total_out = 0;
  strm.zalloc = Z_NULL;
  strm.zfree = Z_NULL;
  if (inflateInit2(&strm, (15+32)) != Z_OK)
    return nil;
  
  while (!done) {
    // Make sure we have enough room and reset the lengths.
    if (strm.total_out >= [decompressed length]) {
      [decompressed increaseLengthBy: half_length];
    }
    strm.next_out = [decompressed mutableBytes] + strm.total_out;
    strm.avail_out = [decompressed length] - strm.total_out;
    // Inflate another chunk.
    status = inflate (&strm, Z_SYNC_FLUSH);
    if (status == Z_STREAM_END) {
      done = YES;
    } else if (status != Z_OK) {
      break;
    }
  }
  
  if (inflateEnd (&strm) != Z_OK)
    return nil;
  // Set real length.
  if (done) {
    [decompressed setLength: strm.total_out];
    return [NSData dataWithData: decompressed];
  }
  return nil;
}

顺便附上gzip压缩:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
+ (NSData*)gzipData:(NSData*)pUncompressedData
{
  if (!pUncompressedData || [pUncompressedData length] == 0)
  {
    NSLog(@"%s: Error: Can't compress an empty or null NSData object.", __func__);
    return nil;
  }
  
  z_stream zlibStreamStruct;
  zlibStreamStruct.zalloc    = Z_NULL; // Set zalloc, zfree, and opaque to Z_NULL so
  zlibStreamStruct.zfree     = Z_NULL; // that when we call deflateInit2 they will be
  zlibStreamStruct.opaque    = Z_NULL; // updated to use default allocation functions.
  zlibStreamStruct.total_out = 0; // Total number of output bytes produced so far
  zlibStreamStruct.next_in   = (Bytef*)[pUncompressedData bytes]; // Pointer to input bytes
  zlibStreamStruct.avail_in  = [pUncompressedData length]; // Number of input bytes left to process
  
  int initError = deflateInit2(&zlibStreamStruct, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY);
  if (initError != Z_OK)
  {
    NSString *errorMsg = nil;
    switch (initError)
    {
      case Z_STREAM_ERROR:
        errorMsg = @"Invalid parameter passed in to function.";
        break;
      case Z_MEM_ERROR:
        errorMsg = @"Insufficient memory.";
        break;
      case Z_VERSION_ERROR:
        errorMsg = @"The version of zlib.h and the version of the library linked do not match.";
        break;
      default:
        errorMsg = @"Unknown error code.";
        break;
    }
    NSLog(@"%s: deflateInit2() Error: \"%@\" Message: \"%s\"", __func__, errorMsg, zlibStreamStruct.msg);

    return nil;
  }
  
  // Create output memory buffer for compressed data. The zlib documentation states that
  // destination buffer size must be at least 0.1% larger than avail_in plus 12 bytes.
  NSMutableData *compressedData = [NSMutableData dataWithLength:[pUncompressedData length] * 1.01 + 12];
  
  int deflateStatus;
  do
  {
    // Store location where next byte should be put in next_out
    zlibStreamStruct.next_out = [compressedData mutableBytes] + zlibStreamStruct.total_out;
    
    // Calculate the amount of remaining free space in the output buffer
    // by subtracting the number of bytes that have been written so far
    // from the buffer's total capacity
    zlibStreamStruct.avail_out = [compressedData length] - zlibStreamStruct.total_out;
    deflateStatus = deflate(&zlibStreamStruct, Z_FINISH);
    
  } while ( deflateStatus == Z_OK );
  
  // Check for zlib error and convert code to usable error message if appropriate
  if (deflateStatus != Z_STREAM_END)
  {
    NSString *errorMsg = nil;
    switch (deflateStatus)
    {
      case Z_ERRNO:
        errorMsg = @"Error occured while reading file.";
        break;
      case Z_STREAM_ERROR:
        errorMsg = @"The stream state was inconsistent (e.g., next_in or next_out was NULL).";
        break;
      case Z_DATA_ERROR:
        errorMsg = @"The deflate data was invalid or incomplete.";
        break;
      case Z_MEM_ERROR:
        errorMsg = @"Memory could not be allocated for processing.";
        break;
      case Z_BUF_ERROR:
        errorMsg = @"Ran out of output buffer for writing compressed bytes.";
        break;
      case Z_VERSION_ERROR:
        errorMsg = @"The version of zlib.h and the version of the library linked do not match.";
        break;
      default:
        errorMsg = @"Unknown error code.";
        break;
    }
    DDLogError(@"%s: zlib error while attempting compression: \"%@\" Message: \"%s\"", __func__, errorMsg, zlibStreamStruct.msg);

    // Free data structures that were dynamically created for the stream.
    deflateEnd(&zlibStreamStruct);
    
    return nil;
  }
  // Free data structures that were dynamically created for the stream.
  deflateEnd(&zlibStreamStruct);
  [compressedData setLength: zlibStreamStruct.total_out];
  DDLogInfo(@"%s: Compressed file from %d KB to %d KB", __func__, [pUncompressedData length]/1024, [compressedData length]/1024);
  
  return compressedData;
}