LZMA(Lempel-Ziv-Markov chain-Algorithm的縮寫)是2001年以來得到發展的一個數據壓縮算法,它用于7-Zip歸檔工具中的7z格式和 Unix-like 下的 xz 格式。它使用類似于LZ77的字典編碼機制,在一般的情況下壓縮率比bzip2為高,用于壓縮的字典文件大小可達4GB。
C++語言寫成的LZMA開放源碼壓縮庫使用了區間編碼支持的LZ77改進壓縮算法以及特殊的用于二進制的預處理程序。LZMA 對數據流、重復序列大小以及重續序列位置單獨進行了壓縮。LZMA支持幾種散列鏈變體、二叉樹以及基數樹作為它的字典查找算法基礎。
LZMA算法引入
對于數據傳輸,傳輸時間和傳輸質量是主要的兩個參考維度。對于傳輸時間的壓縮,數據壓縮又是一個很好地可選項。目前正在處理藍牙BLE(Bluetooth low energy (Bluetooth LE, BLE, marketed as Bluetooth Smart[1]))調試,其中數據傳輸過程中,發現由于速度限制,傳輸時間較長,為了縮短傳輸時間,想利用壓縮算法對所要傳輸的數據進行壓縮處理后再進行傳輸,壓縮完成之后再進行傳輸,以提高傳輸效率及節省傳輸時間。經過查閱各種資料,初步使用LZMA壓縮算法進行壓縮。
之所以選擇LZMA算法進行壓縮處理,原因有以下幾點:
- 開源
- iOS & Android平臺均支持,可夸平臺使用
- 使用廣泛穩定,7zip即采用該算法及衍生算法
- 壓縮效率較高
- 多線程支持
iOS引入使用LZMA壓縮算法方法
選項1. 利用系統默認支持的LZMA壓縮算法
Apple提供了一套通用的無損壓縮算法,其中就支持LZMA、LZMA2壓縮.
The libcompression library provides an API for two styles of data compression:
- Block compression, where all of the input data is compressed or decompressed by one call to the compression or decompression function.
- Streaming compression, where the compression or decompression function is called repeatedly to compress or decompress data from a source buffer to a destination buffer. Between calls, processed data is moved out of the destination buffer and new data is loaded into the source buffer.
支持的壓縮類型
- Block Compression
- Stream Compression
支持的平臺如下:
- iOS 9.0+
- macOS 10.11+
- tvOS 9.0+
- watchOS 2.0+
支持的壓縮算法
- COMPRESSION_LZ4
- COMPRESSION_ZLIB
- COMPRESSION_LZMA
- COMPRESSION_LZFSE
提供的代碼調用很簡單,我根據我的需要,所使用的方法如下:
size_t compression_encode_buffer(uint8_t *restrict dst_buffer, size_t dst_size, const uint8_t *restrict src_buffer, size_t src_size, void *restrict scratch_buffer, compression_algorithm algorithm);
size_t compression_decode_buffer(uint8_t *restrict dst_buffer, size_t dst_size, const uint8_t *restrict src_buffer, size_t src_size, void *restrict scratch_buffer, compression_algorithm algorithm);
詳細的調用代碼如下
- (void)testLZMA {
// Data source file path.
NSString *sourceFilePath = [NSString stringWithFormat:@"%@/source_data.txt", SYSTEM_DOCUMENT_PATH];
// Compressed file path.
NSString *zipFilePath = [NSString stringWithFormat:@"%@/compressed_data.7z", SYSTEM_DOCUMENT_PATH];
NSData *fileData = [NSData dataWithContentsOfFile:sourceFilePath];
DDLogDebug(@"Before compress: %ld bytes", fileData.length);
uint8_t dstBuffer[fileData.length];
memset(dstBuffer, 0, fileData.length);
size_t compressResultLength = compression_encode_buffer(dstBuffer, fileData.length, [fileData bytes], fileData.length, NULL, COMPRESSION_LZMA);
if(compressResultLength > 0) {
NSData *dataAfterCompress = [NSData dataWithBytes:dstBuffer length:compressResultLength];
DDLogDebug(@"Compress successfully. After compress:%ld bytes", dataAfterCompress.length;
// Write compressed data into file.
[dataAfterCompress writeToFile:zipFilePath atomically:YES];
} else {
DDLogError(@"Compress FAILED!!!");
}
}
該方法集成使用起來非常簡單,對于基本的壓縮需求足夠可以滿足,且不會對App的大小造成太大影響,不會很大增加,如果沒有特殊需求,該方法是首選。
選項2. 集成第三方庫LzmaSDKOjbcFramework
這是我最先走的一條路,通過查閱相關資料,引入相關的開源庫,自己實現了一個支持LZMA壓縮算法的iOS工程用于Build Framework,現已開源到Github上,即LzmaSDKOjbcFramework。
雖然最終采用的方案一,但在制作LzmaSDKOjbcFramework過程中,也有一些收獲,現分享給大家,愿對大家有些幫助。
最初找到的LZMA的iOS支持庫是 LzmaSDKObjC,但是這個庫在引入開發工程中過程中,由于使用的cocoaPods, 必須使用use_frameworks!
才可以使用,但是由于podfile中存在其他引入的第三方庫,這些庫不適用use_frameworks!
限制。
此時陷入兩難境地,使用use_frameworks!
導致其他不支持framework的庫不可用,如果不使用,LzmaSDKObjC則會報如下錯誤:
Codec was not compiled in or stripped by static linking.
Make sure you are using 'use_frameworks!' and/or dynamic linking ...
而CocoaPods又不支持針對某一第三方庫來規定使用use_frameworks!
既然這樣,我打算自己創建一個iOS Framework工程開源,供團隊內部及所有人方便使用。
使用步驟
Step1
在LzmaSDKOjbcFramework工程目錄下,由于工程需要Inlineobjc
庫,所以使用CocoaPods進行安裝,命令行執行如下命令:
$ pod install
Step2
打開workspace工程文件,xCode中看到的內容如下:
Step3
Archive工程并導出LzmaSDKObjC.framework
文件到所需的工程路徑下使用,使用如下:
- (void)testLZMA {
NSString *sourceFilePath = [NSString stringWithFormat:@"%@/source_data.txt", SYSTEM_DOCUMENT_PATH];
NSString *zipFilePath = [NSString stringWithFormat:@"%@/compressed_data.7z", SYSTEM_DOCUMENT_PATH];
DDLogDebug(@"\n\n ********** LZMA ********** \nSrc File: %@\n7Zip File:%@\n\n", sourceFilePath, zipFilePath);
// Create writer
LzmaSDKObjCWriter * writer = [[LzmaSDKObjCWriter alloc] initWithFileURL:[NSURL fileURLWithPath:zipFilePath]];
// Add file data's or paths
// [writer addData:[NSData ...] forPath:@"MyArchiveFileName.txt"]; // Add file data
[writer addPath:sourceFilePath forPath:@"."]; // Add file at path
// [writer addPath:@"/Path/SomeDirectory" forPath:@"SomeDirectory"]; // Recursively add directory with all contents
// Setup writer
writer.delegate = self; // Track progress
// writer.passwordGetter = ^NSString*(void) { // Password getter
// return @"1234";
// };
// Optional settings
writer.method = LzmaSDKObjCMethodLZMA; // or LzmaSDKObjCMethodLZMA
writer.solid = YES;
writer.compressionLevel = 7;
writer.encodeContent = YES;
writer.encodeHeader = YES;
writer.compressHeader = YES;
writer.compressHeaderFull = YES;
writer.writeModificationTime = NO;
writer.writeCreationTime = NO;
writer.writeAccessTime = NO;
// Open archive file
NSError * error = nil;
[writer open:&error];
// Write archive within current thread
[writer write];
}
該方式的優點是支持的可選項較廣,可以廣泛的定制各種參數,但缺點是導入庫后會導致應用包的體積變大,所以需要根據自身需求來選擇。
ENJOY.