OC实现GZIP压缩及解压缩

这恍恍的天日晒的大地嗞嗞的作响。这湉湉的阴雨下的祖国母亲到处洪水泛滥。人本不该有三六九等,可这丑陋的阴雨竟然选择性的泄洪到无辜的县区以示人却有三六九等。谁的财产不是财产,谁的生命不是生命?谁特妈的不是母亲养大的?

一首罗刹海市,单曲循环了一周了,这调调听的真特么的解恨。与其说是骂娱乐圈的那些营营狗狗,可你我为啥不将其骂的范围再上升一个层次呢?他到底在骂批判谁或者哪个阶级呢?越听越上头,越听越觉的骂的很舒服。大抵我等屁民也就这点快感了吧,其他的岂敢声张呢?

恬不知耻却还能后勇,一个屁十字会,月薪几十万等这都是什么蝇狗?还来呼吁募捐?那些年还有些做善事让自己内心温暖的想法或者是举动,为的是陶冶自己的情操,艹,陶冶个驴鸡!把生我的和我生的通过自己的努力养好,不香吗?

刹车!

最近因为IM中信令数据过大导致网络层传输数据过大的问题进行了优化,原本3M的数据压缩后以400多KB进行传输,这也很香!当然数据量越大效率越高!来吧,马户,直接吃鸡!

头文件实现:[内存数据解压缩,你可以二次改造为文件目录解压缩]

//
//  NSZip.h
//
//  Created by carbonzhao on 2023/8/1.
//

#import 
#import 

NS_ASSUME_NONNULL_BEGIN

//此类主要为内存压缩,非文件目录解压缩。可进行稍微改造实现指定目录文件gzip解压缩,此处不实现。
@interface NSZip : NSObject
+ (id)zip;

//将指定的data进行gzip压缩。若需要进行Zip或者其他类型的压缩请调整deflateInit2函数第4个参数值。
- (NSData *)zip:(NSData *)data error:(NSError **)err;

//将指定的数据进行gzip解压缩。
- (NSData *)unZip:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err;
+ (NSData *)unZip:(NSData*)data error:(NSError **)err;

//指定sourcePath目录的gzip压缩包解压到destinationPath指定的目录中。用于文件解压缩。
+ (BOOL)unZip:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err;
@end

NS_ASSUME_NONNULL_END

.m文件实现:

//
//  NSZip.m
//
//  Created by carbonzhao on 2023/8/1.
//

#import "NSZip.h"
#define DATA_CHUNK_SIZE 262144

@interface NSZip ()
{
    BOOL zipReady;
    z_stream zStream;
}

@property (atomic, assign, readonly) BOOL zipReady;
@end

@implementation NSZip

@synthesize zipReady;

+ (id)zip
{
    NSZip *zip = [[NSZip alloc] init];
    [zip resetZip];
    return zip;
}


- (void)dealloc
{
    if (zipReady)
    {
        [self closeZip];
    }
}

- (NSError *)resetZip
{
    if (zipReady)
    {
        return nil;
    }
    // Setup the inflate stream
    zStream.zalloc = Z_NULL;
    zStream.zfree = Z_NULL;
    zStream.opaque = Z_NULL;
    zStream.avail_in = 0;
    zStream.next_in = 0;
    int status = inflateInit2(&zStream, (15+32));
    if (status != Z_OK)
    {
        return [[self class] inflateErrorWithCode:status];
    }
    zipReady = YES;
    return nil;
}

- (NSError *)closeZip
{
    if (!zipReady)
    {
        return nil;
    }
    // Close the inflate stream
    zipReady = NO;
    int status = inflateEnd(&zStream);
    if (status != Z_OK)
    {
        return [[self class] inflateErrorWithCode:status];
    }
    return nil;
}


- (NSData *)zip:(NSData *)data error:(NSError **)err
{
    if (data && data.length > 0)
    {
        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+16,MAX_MEM_LEVEL,Z_DEFAULT_STRATEGY);
/*       历史缓冲区最大尺寸,值为 2^windowBits,
        windowBits 的值为 8~15 时,deflate() 方法生成 zlib 格式的数据,
        当 windowBits 为 31 时 deflate() 方法生成 gzip 格式。
        当取值为 -15 ~ -8 时,deflate() 生成纯 deflate 算法压缩数据(不包含 zlib 和 gzip 格式头和尾)

        OSStatus status = deflateInit2(&zStream, Z_DEFAULT_COMPRESSION,Z_DEFLATED, 31, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);*/
        
        if (status != Z_OK)
        {
            return nil;
        }
        
        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)) {
            *err = [[self class] inflateErrorWithCode:status];
            return nil;
        }
        
        compressedData.length = zStream.total_out;
        
        return compressedData;
    }
    return nil;
}

- (NSData *)unZip:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err
{
    if (length == 0) return nil;
    
    NSUInteger halfLength = length/2;
    NSMutableData *outputData = [NSMutableData dataWithLength:length+halfLength];

    int status;
    
    zStream.next_in = bytes;
    zStream.avail_in = (unsigned int)length;
    zStream.avail_out = 0;
    
    NSUInteger bytesProcessedAlready = zStream.total_out;
    while (zStream.avail_in != 0)
    {
        if (zStream.total_out-bytesProcessedAlready >= [outputData length]) {
            [outputData increaseLengthBy:halfLength];
        }
        
        zStream.next_out = (Bytef*)[outputData mutableBytes] + zStream.total_out-bytesProcessedAlready;
        zStream.avail_out = (unsigned int)([outputData length] - (zStream.total_out-bytesProcessedAlready));
        
        status = inflate(&zStream, Z_NO_FLUSH);
        
        if (status == Z_STREAM_END)
        {
            break;
        }
        else if (status != Z_OK)
        {
            if (err)
            {
                *err = [[self class] inflateErrorWithCode:status];
            }
            return nil;
        }
    }
    
    // Set real length
    [outputData setLength: zStream.total_out-bytesProcessedAlready];
    return outputData;
}


+ (NSData *)unZip:(NSData*)data error:(NSError **)err
{
    NSError *theError = nil;
    NSData *outputData = [[NSZip zip] unZip:(Bytef *)[data bytes] length:[data length] error:&theError];
    if (theError)
    {
        if (err)
        {
            *err = theError;
        }
        return nil;
    }
    return outputData;
}

+ (BOOL)unZip:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err
{
    NSFileManager *fileManager = [[NSFileManager alloc] init];

    // Create an empty file at the destination path
    if (![fileManager createFileAtPath:destinationPath contents:[NSData data] attributes:nil]) {
        if (err)
        {
            *err = [NSError errorWithDomain:@"NSZipErrorDomain" code:11 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were to create a file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,nil]];
        }
        return NO;
    }
    
    // Ensure the source file exists
    if (![fileManager fileExistsAtPath:sourcePath])
    {
        if (err)
        {
            *err = [NSError errorWithDomain:@"NSZipErrorDomain" code:11 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed the file does not exist",sourcePath],NSLocalizedDescriptionKey,nil]];
        }
        return NO;
    }
    
    UInt8 inputData[DATA_CHUNK_SIZE];
    NSData *outputData;
    NSInteger readLength;
    NSError *theError = nil;
    

    NSZip *decompressor = [NSZip zip];

    NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:sourcePath];
    [inputStream open];
    NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:destinationPath append:NO];
    [outputStream open];
    
    while ([decompressor zipReady])
    {
        // Read some data from the file
        readLength = [inputStream read:inputData maxLength:DATA_CHUNK_SIZE];
        
        // Make sure nothing went wrong
        if ([inputStream streamStatus] == NSStreamStatusError)
        {
            if (err)
            {
                *err = [NSError errorWithDomain:@"NSZipErrorDomain" code:11 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were unable to read from the source data file",sourcePath],NSLocalizedDescriptionKey,[inputStream streamError],NSUnderlyingErrorKey,nil]];
            }
            [decompressor closeZip];
            return NO;
        }
        // Have we reached the end of the input data?
        if (!readLength)
        {
            break;
        }

        // Attempt to inflate the chunk of data
        outputData = [decompressor unZip:inputData length:(NSUInteger)readLength error:&theError];
        if (theError)
        {
            if (err)
            {
                *err = theError;
            }
            [decompressor closeZip];
            return NO;
        }
        
        // Write the inflated data out to the destination file
        [outputStream write:(Bytef*)[outputData bytes] maxLength:[outputData length]];
        
        // Make sure nothing went wrong
        if ([inputStream streamStatus] == NSStreamStatusError) {
            if (err)
            {
                *err = [NSError errorWithDomain:@"NSZipErrorDomain" code:11 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were unable to write to the destination data file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,[outputStream streamError],NSUnderlyingErrorKey,nil]];
            }
            [decompressor closeZip];
            return NO;
        }
        
    }
    
    [inputStream close];
    [outputStream close];

    NSError *error = [decompressor closeZip];
    if (error)
    {
        if (err)
        {
            *err = error;
        }
        return NO;
    }

    return YES;
}


+ (NSError *)inflateErrorWithCode:(int)code
{
    return [NSError errorWithDomain:@"NSZipErrorDomain" code:11 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of data failed with code %d",code],NSLocalizedDescriptionKey,nil]];
}
@end

使用方法:

1、gzip压缩[默认为gzip压缩,其他压缩看代码注释]

//text为NSString类型,将其压缩后通过Websocket传输
NSError *err = nil;
NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];
NSData *zipData = [[NSZip zip] zip:data error:&err];

[weakSelf.linkedSocket sendData:zipData error:nil];

2、解压

//message为websocket返回的NSData数据,进行解压
NSError *err= nil;
NSData *data = [NSZip unZip:message error:&err];
if (!err)
{
    NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

//解压后的NSString分发到UI层面进行使用
     [weakSelf dispatchData:msg];
}

你可能感兴趣的:(ios)