How to gzip Data in Memory Using Objective-C

本文介绍了如何利用Objective-C和zlib库实现内存中数据的压缩,并提供了详细的代码示例。重点包括代码的注释、错误处理、插件式设计以及用户友好的文档和错误信息,使开发者能够快速理解和使用该工具。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

I recently had to write a utility for compressing data in memory using the gzip format. Thankfully, there’s a C library called zlib you can use to do the actual compression (and thankfully you can link to libz.dylib on the iPhone). Using the library is not trivial, however, so I had to spend a day reading the zlib headers/documentation and also searching for examples from other developers (one example shared by Robbie Hanson was particularly helpful).

While Robbie’s example is great, I wanted something a bit more robust and easier to “plug in” to any existing project. As part of making it “plug ‘n play,” I also wanted to make it developer-friendly: if something goes wrong, the utility should be helpful in solving the problem instead of just exiting with a cryptic error code. That means adding a healthy amount of documentation and descriptive error message logging so that Joe Developer–who just wanted to copy and paste the utility into his project and move on–can quickly understand the code and the error message if problems come up.

Here’s an example of how you would use the class:

  1. /** 
  2.  @file LFCGzipUtility.h 
  3.  @author Clint Harris (www.clintharris.net) 
  4.  
  5.  Note: The code in this file has been commented so as to be compatible with 
  6.  Doxygen, a tool for automatically generating HTML-based documentation from 
  7.  source code. See http://www.doxygen.org for more info. 
  8.  */  
  9.   
  10. #import <Foundation/Foundation.h>  
  11. #import "zlib.h"  
  12.   
  13. @interface LFCGzipUtility : NSObject  
  14. {  
  15.   
  16. }  
  17.   
  18. /***************************************************************************//** 
  19.  Uses zlib to compress the given data. Note that gzip headers will be added so 
  20.  that the data can be easily decompressed using a tool like WinZip, gunzip, etc. 
  21.  
  22.  Note: Special thanks to Robbie Hanson of Deusty Designs for sharing sample code 
  23.  showing how deflateInit2() can be used to make zlib generate a compressed file 
  24.  with gzip headers: 
  25.  
  26. http://deusty.blogspot.com/2007/07/gzip-compressiondecompression.html 
  27.  
  28.  @param pUncompressedData memory buffer of bytes to compress 
  29.  @return Compressed data as an NSData object 
  30.  */  
  31. +(NSData*) gzipData: (NSData*)pUncompressedData;  
  32.   
  33. @end  
  1. /** 
  2.  @file LFCGzipUtility.m 
  3.  @author Clint Harris (www.clintharris.net) 
  4.  
  5.  Note: The code in this file has been commented so as to be compatible with 
  6.  Doxygen, a tool for automatically generating HTML-based documentation from 
  7.  source code. See http://www.doxygen.org for more info. 
  8.  */  
  9.   
  10. #import "LFCGzipUtility.h"  
  11.   
  12. @implementation LFCGzipUtility  
  13.   
  14. /******************************************************************************* 
  15.  See header for documentation. 
  16.  */  
  17. +(NSData*) gzipData: (NSData*)pUncompressedData  
  18. {  
  19.     /* 
  20.      Special thanks to Robbie Hanson of Deusty Designs for sharing sample code 
  21.      showing how deflateInit2() can be used to make zlib generate a compressed 
  22.      file with gzip headers: 
  23.  
  24. http://deusty.blogspot.com/2007/07/gzip-compressiondecompression.html 
  25.  
  26.      */  
  27.   
  28.     if (!pUncompressedData || [pUncompressedData length] == 0)  
  29.     {  
  30.         NSLog(@"%s: Error: Can't compress an empty or null NSData object.", __func__);  
  31.         return nil;  
  32.     }  
  33.   
  34.     /* Before we can begin compressing (aka "deflating") data using the zlib 
  35.      functions, we must initialize zlib. Normally this is done by calling the 
  36.      deflateInit() function; in this case, however, we'll use deflateInit2() so 
  37.      that the compressed data will have gzip headers. This will make it easy to 
  38.      decompress the data later using a tool like gunzip, WinZip, etc. 
  39.  
  40.      deflateInit2() accepts many parameters, the first of which is a C struct of 
  41.      type "z_stream" defined in zlib.h. The properties of this struct are used to 
  42.      control how the compression algorithms work. z_stream is also used to 
  43.      maintain pointers to the "input" and "output" byte buffers (next_in/out) as 
  44.      well as information about how many bytes have been processed, how many are 
  45.      left to process, etc. */  
  46.     z_stream zlibStreamStruct;  
  47.     zlibStreamStruct.zalloc    = Z_NULL; // Set zalloc, zfree, and opaque to Z_NULL so  
  48.     zlibStreamStruct.zfree     = Z_NULL; // that when we call deflateInit2 they will be  
  49.     zlibStreamStruct.opaque    = Z_NULL; // updated to use default allocation functions.  
  50.     zlibStreamStruct.total_out = 0; // Total number of output bytes produced so far  
  51.     zlibStreamStruct.next_in   = (Bytef*)[pUncompressedData bytes]; // Pointer to input bytes  
  52.     zlibStreamStruct.avail_in  = [pUncompressedData length]; // Number of input bytes left to process  
  53.   
  54.     /* Initialize the zlib deflation (i.e. compression) internals with deflateInit2(). 
  55.      The parameters are as follows: 
  56.  
  57.      z_streamp strm - Pointer to a zstream struct 
  58.      int level      - Compression level. Must be Z_DEFAULT_COMPRESSION, or between 
  59.                       0 and 9: 1 gives best speed, 9 gives best compression, 0 gives 
  60.                       no compression. 
  61.      int method     - Compression method. Only method supported is "Z_DEFLATED". 
  62.      int windowBits - Base two logarithm of the maximum window size (the size of 
  63.                       the history buffer). It should be in the range 8..15. Add  
  64.                       16 to windowBits to write a simple gzip header and trailer  
  65.                       around the compressed data instead of a zlib wrapper. The  
  66.                       gzip header will have no file name, no extra data, no comment,  
  67.                       no modification time (set to zero), no header crc, and the  
  68.                       operating system will be set to 255 (unknown).  
  69.      int memLevel   - Amount of memory allocated for internal compression state. 
  70.                       1 uses minimum memory but is slow and reduces compression 
  71.                       ratio; 9 uses maximum memory for optimal speed. Default value 
  72.                       is 8. 
  73.      int strategy   - Used to tune the compression algorithm. Use the value 
  74.                       Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data 
  75.                       produced by a filter (or predictor), or Z_HUFFMAN_ONLY to 
  76.                       force Huffman encoding only (no string match) */  
  77.     int initError = deflateInit2(&zlibStreamStruct, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY);  
  78.     if (initError != Z_OK)  
  79.     {  
  80.         NSString *errorMsg = nil;  
  81.         switch (initError)  
  82.         {  
  83.             case Z_STREAM_ERROR:  
  84.                 errorMsg = @"Invalid parameter passed in to function.";  
  85.                 break;  
  86.             case Z_MEM_ERROR:  
  87.                 errorMsg = @"Insufficient memory.";  
  88.                 break;  
  89.             case Z_VERSION_ERROR:  
  90.                 errorMsg = @"The version of zlib.h and the version of the library linked do not match.";  
  91.                 break;  
  92.             default:  
  93.                 errorMsg = @"Unknown error code.";  
  94.                 break;  
  95.         }  
  96.         NSLog(@"%s: deflateInit2() Error: \"%@\" Message: \"%s\"", __func__, errorMsg, zlibStreamStruct.msg);  
  97.         [errorMsg release];  
  98.         return nil;  
  99.     }  
  100.   
  101.     // Create output memory buffer for compressed data. The zlib documentation states that  
  102.     // destination buffer size must be at least 0.1% larger than avail_in plus 12 bytes.  
  103.     NSMutableData *compressedData = [NSMutableData dataWithLength:[pUncompressedData length] * 1.01 + 12];  
  104.   
  105.     int deflateStatus;  
  106.     do  
  107.     {  
  108.         // Store location where next byte should be put in next_out  
  109.         zlibStreamStruct.next_out = [compressedData mutableBytes] + zlibStreamStruct.total_out;  
  110.   
  111.         // Calculate the amount of remaining free space in the output buffer  
  112.         // by subtracting the number of bytes that have been written so far  
  113.         // from the buffer's total capacity  
  114.         zlibStreamStruct.avail_out = [compressedData length] - zlibStreamStruct.total_out;  
  115.   
  116.         /* deflate() compresses as much data as possible, and stops/returns when 
  117.          the input buffer becomes empty or the output buffer becomes full. If 
  118.          deflate() returns Z_OK, it means that there are more bytes left to 
  119.          compress in the input buffer but the output buffer is full; the output 
  120.          buffer should be expanded and deflate should be called again (i.e., the 
  121.          loop should continue to rune). If deflate() returns Z_STREAM_END, the 
  122.          end of the input stream was reached (i.e.g, all of the data has been 
  123.          compressed) and the loop should stop. */  
  124.         deflateStatus = deflate(&zlibStreamStruct, Z_FINISH);  
  125.   
  126.     } while ( deflateStatus == Z_OK );        
  127.   
  128.     // Check for zlib error and convert code to usable error message if appropriate  
  129.     if (deflateStatus != Z_STREAM_END)  
  130.     {  
  131.         NSString *errorMsg = nil;  
  132.         switch (deflateStatus)  
  133.         {  
  134.             case Z_ERRNO:  
  135.                 errorMsg = @"Error occured while reading file.";  
  136.                 break;  
  137.             case Z_STREAM_ERROR:  
  138.                 errorMsg = @"The stream state was inconsistent (e.g., next_in or next_out was NULL).";  
  139.                 break;  
  140.             case Z_DATA_ERROR:  
  141.                 errorMsg = @"The deflate data was invalid or incomplete.";  
  142.                 break;  
  143.             case Z_MEM_ERROR:  
  144.                 errorMsg = @"Memory could not be allocated for processing.";  
  145.                 break;  
  146.             case Z_BUF_ERROR:  
  147.                 errorMsg = @"Ran out of output buffer for writing compressed bytes.";  
  148.                 break;  
  149.             case Z_VERSION_ERROR:  
  150.                 errorMsg = @"The version of zlib.h and the version of the library linked do not match.";  
  151.                 break;  
  152.             default:  
  153.                 errorMsg = @"Unknown error code.";  
  154.                 break;  
  155.         }  
  156.         NSLog(@"%s: zlib error while attempting compression: \"%@\" Message: \"%s\"", __func__, errorMsg, zlibStreamStruct.msg);  
  157.         [errorMsg release];  
  158.   
  159.         // Free data structures that were dynamically created for the stream.  
  160.         deflateEnd(&zlibStreamStruct);  
  161.   
  162.         return nil;  
  163.     }  
  164.     // Free data structures that were dynamically created for the stream.  
  165.     deflateEnd(&zlibStreamStruct);  
  166.     [compressedData setLength: zlibStreamStruct.total_out];  
  167.     NSLog(@"%s: Compressed file from %d KB to %d KB", __func__, [pUncompressedData length]/1024, [compressedData length]/1024);  
  168.   
  169.     return compressedData;  
  170. }  
  171.   
  172. @end  
Clint Harris is an independent software consultant living in Brooklyn, New York. He can be contacted directly at  ten.sirrahtnilc@tnilc.


转自:http://www.clintharris.net/2009/how-to-gzip-data-in-memory-using-objective-c/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值