iOS 使用 zlib 庫實(shí)現(xiàn) HTTP 請求數(shù)據(jù)壓縮

iOS 使用 zlib 庫實(shí)現(xiàn)請求數(shù)據(jù)壓縮


1.Content-Encoding

Accept-Encoding 和 Content-Encoding 是 HTTP 中的一對頭部字段,用來標(biāo)識「請求發(fā)送方可接受的響應(yīng)數(shù)據(jù)編碼格式」、「請求/響應(yīng)方發(fā)送的 HTTP Body 編碼格式」。接收方拿到請求/響應(yīng)后,根據(jù) Content-Encoding 對應(yīng)的數(shù)據(jù)格式進(jìn)行解壓數(shù)據(jù)。壓縮的目的是為了優(yōu)化傳輸內(nèi)容的大小,減少服務(wù)端流量壓力。

Content-Encoding 的取值有 gzip、defalte、compress、identity、br 等,具體用到哪個需要跟服務(wù)端協(xié)商好。Xcode 提供的 zlib 可以實(shí)現(xiàn)這里的 gzipdefalte 這兩種編碼格式。

gzip
一種由文件壓縮程序「Gzip,GUN zip」產(chǎn)生的編碼格式,描述于 RFC 1952。這種編碼格式是一種具有 32 位 CRC 的 Lempel-Ziv 編碼(LZ77);

deflate
由定義于 RFC 1950 的「ZLIB」編碼格式與 RFC 1951 中描述的「DEFLATE」壓縮機(jī)制組合而成的產(chǎn)物;

由于 HTTP 1.1 的命名失誤,deflate 當(dāng)初應(yīng)該命名為 zlib,因?yàn)槠浔旧砭褪鞘褂?zlib 格式編碼,用 deflate 造成了與 DEFLATE 算法的混淆,事實(shí)上 Content-Encodinggzipdeflate 時都可能使用 DEFLATE 算法。

2. zlib庫的使用

下面介紹一下使用 zlib 庫對 NSData 進(jìn)行壓縮操作的基本過程以及注意事項(xiàng):

2.1 引入頭文件

#import "zlib.h"

2.2 初始化

在開始使用 zlib.h 提供的方法壓縮數(shù)據(jù)之前,必需做一些初始化工作。一般情況下初始化需要調(diào)用 deflateInit() 或者 deflateInit2() 函數(shù),用 deflateInit() 初始化后壓縮產(chǎn)生 zlib 編碼格式,對應(yīng) Content-Encoding 中的 deflate,而 deflateInit2() 通過不同參數(shù)配置可以產(chǎn)生對應(yīng) Content-Encoding 中的 gzipdeflate 的不同壓縮格式。

deflateInit2() 函數(shù)有很多個參數(shù),第一個是個 C 結(jié)構(gòu)體 z_stream,定義在 zlib.h 中。

z_stream 這個結(jié)構(gòu)體的成員用于控制壓縮算法的工作方式,同時也維護(hù)了兩組輸入輸出指針 next_in/out,還有關(guān)于已處理的字節(jié)數(shù)和剩余未處理的字節(jié)數(shù)等信息。

2.2.1 初始化 z_stream

z_stream 的初始化過程如下,其中 zalloc、zfree、opaque 設(shè)置為 NULL 這樣之后的 deflateInit2() 方法會把這些指針更新為默認(rèn)的內(nèi)存管理函數(shù)。

z_stream zStream;
bzero(&zStream, sizeof(zStream));

zStream.zalloc = Z_NULL;
zStream.zfree = Z_NULL;
zStream.opaque = Z_NULL;
// 剩余的需要壓縮字指針
zStream.next_in = (Bytef *) data.bytes;
// 剩余的需要壓縮字節(jié)數(shù)
zStream.avail_in = (uInt) data.length;
// 目前已經(jīng)輸出的字節(jié)數(shù)
zStream.total_out = 0;

2.2.2 調(diào)用 deflateInit2()

先看一下 deflateInit2() 的定義

z_streamp strm: z_stream 指針;

int level: 壓縮等級,必需為 Z_DEFAULT_COMPRESSION 或者 0 ~ 9 的整數(shù),1為最快,9為最大限度壓縮,0為不壓縮,數(shù)字越大越耗時;

int method: 壓縮算法,只支持 Z_DEFLATED;

int windowBits: 歷史緩沖區(qū)最大尺寸,值為 2^windowBits, windowBits 的值為 8~15 時,deflate() 方法生成 zlib 格式的數(shù)據(jù),當(dāng) windowBits 為 31 時 deflate() 方法生成 gzip 格式。當(dāng)取值為 -15 ~ -8 時,deflate() 生成純 deflate 算法壓縮數(shù)據(jù)(不包含 zlib 和 gzip 格式頭和尾)

int strategy: 用于調(diào)整壓縮算法,一般使用 Z_DEFAULT_STRATEGY

ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
                                     int  level,
                                     int  method,
                                     int  windowBits,
                                     int  memLevel,
                                     int  strategy));

以生成 gzip 格式輸出為例:

OSStatus status = deflateInit2(&zStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
    
if (status != Z_OK) {
    return nil;
}

2.3 生成壓縮數(shù)據(jù)

完成初始化之后,循環(huán)調(diào)用 deflate () 方法不斷壓縮數(shù)據(jù)并填充到 NSMutableData 中,直到 deflate () 返回結(jié)果不為 Z_BUF_ERROR 或者 Z_OK 為止。

static NSInteger kZlibCompressChunkSize = 2048;
NSMutableData *compressedData = [NSMutableData dataWithLength:kZlibCompressChunkSize];
do {
    if ((status == Z_BUF_ERROR) || (zStream.total_out == compressedData.length)) {
        [compressedData increaseLengthBy:kZlibCompressChunkSize];
    }
    zStream.next_out = (Bytef *)compressedData.bytes + zStream.total_out;
    zStream.avail_out = (uInt)(compressedData.length - zStream.total_out);
    status = deflate(&zStream, Z_FINISH);
} while ((status == Z_BUF_ERROR) || (status == Z_OK));
    
status = deflateEnd(&zStream);
    
if ((status != Z_OK) && (status != Z_STREAM_END)) {
    return nil;
}
    
compressedData.length = zStream.total_out;

3. 完成后的壓縮方法

- (NSData *)zlibCompressedData:(NSData *)data
{
    if (data.length == 0) return data;
    
    z_stream zStream;
    bzero(&zStream, sizeof(zStream));
    
    zStream.zalloc = Z_NULL;
    zStream.zfree = Z_NULL;
    zStream.opaque = Z_NULL;
    zStream.next_in = (Bytef *) data.bytes;
    zStream.avail_in = (uInt) data.length;
    zStream.total_out = 0;
    
    OSStatus status = deflateInit2(&zStream,
                                   Z_DEFAULT_COMPRESSION,
                                   Z_DEFLATED, MAX_WBITS,
                                   MAX_MEM_LEVEL,
                                   Z_DEFAULT_STRATEGY);
    
    if (status != Z_OK) {
        return nil;
    }
    
    static NSInteger kZlibCompressChunkSize = 2048;
    NSMutableData *compressedData = [NSMutableData dataWithLength:kZlibCompressChunkSize];
    do {
        if ((status == Z_BUF_ERROR) || (zStream.total_out == compressedData.length)) {
            [compressedData increaseLengthBy:kZlibCompressChunkSize];
        }
        zStream.next_out = (Bytef *)compressedData.bytes + zStream.total_out;
        zStream.avail_out = (uInt)(compressedData.length - zStream.total_out);
        status = deflate(&zStream, Z_FINISH);
    } while ((status == Z_BUF_ERROR) || (status == Z_OK));
    
    status = deflateEnd(&zStream);
    
    if ((status != Z_OK) && (status != Z_STREAM_END)) {
        return nil;
    }
    
    compressedData.length = zStream.total_out;
    
    return compressedData;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、壓縮算法 1.zip 以下參考他發(fā)明了 zip 壓縮格式,功成名就之際死于墮落壓縮大戰(zhàn)真相 轉(zhuǎn)載自2004.1...
    合肥黑閱讀 6,089評論 0 5
  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測試 ...
    KeKeMars閱讀 6,597評論 0 6
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,525評論 19 139
  • 今天是好日子,是我開始個人的第三次「好報寫作營」之旅。 回首前兩次的寫作營,第一次是心血來潮報的,對于沒有接受過任...
    N追夢人_劉蘇立S閱讀 357評論 2 1
  • 最近靠譜一詞在自己的視覺世界里總會重復(fù)的出現(xiàn),大腦對信息的過濾最終停留在這個詞終是有存在的意義,靠譜這個詞用在自己...
    把耳朵叫醒了閱讀 314評論 0 1

友情鏈接更多精彩內(nèi)容