iOS解压Windows生成的gzip文件时触发UNZ_BADZIPFILE(-103)错误求助
问题
我们用C# Windows控制台应用,通过System.IO.Compression.GZipStream压缩SQLite文件,生成的.gzip文件上传到服务器后供iOS端下载。现在遇到的问题是:iOS应用用SSZipArchive库解压时触发UNZ_BADZIPFILE(-103)错误,但在Mac上手动下载该文件后,用SSZipArchive能正常解压。想问是否需要做编码处理,让iOS识别Windows生成的gzip文件?
C# 压缩代码
FileInfo fileToBeGZipped = new FileInfo(filePath); FileInfo gzipFileName = new FileInfo(string.Concat(fileToBeGZipped.FullName, ".gzip")); using (FileStream fileToBeZippedAsStream = fileToBeGZipped.OpenRead()) { using (FileStream gzipTargetAsStream = gzipFileName.Create()) { using (GZipStream gzipStream = new GZipStream(gzipTargetAsStream, CompressionMode.Compress)) { try { fileToBeZippedAsStream.CopyTo(gzipStream); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } }
iOS 解压代码
if (![SSZipArchive unzipFileAtPath:[downloadedStoreURL path] toDestination:[destinationURL path] delegate:nil]) { CLHLog(@"Couldn't unzip downloaded database"); return NO; }
错误信息
Error listing contents of archive: Error Domain=UZKErrorDomain Code=-103 "Bad zip file" UserInfo={NSLocalizedFailureReason=Bad zip file, NSLocalizedRecoverySuggestion=Error opening zip file /var/mobile/Containers/Data/Application/1DD4F888-0171-42D0-AC69-E0D497AC3943/Library/InspectionForms_compresed.sqlite.gzip, NSURL=file:///var/mobile/Containers/Data/Application/1DD4F888-0171-42D0-AC69-E0D497AC3943/Library/InspectionForms_compresed.sqlite.gzip, NSLocalizedDescription=Bad zip file}
解决方案
这个问题不需要额外编码处理,核心矛盾是格式适配问题:
SSZipArchive的定位是处理ZIP格式,而非GZIP
GZip和Zip是两种完全不同的压缩标准:- 你的C#代码生成的是单文件GZIP压缩包,属于流式压缩格式
- SSZipArchive主打多文件ZIP归档解压,Mac端环境对GZIP的兼容逻辑更宽松,iOS端的底层实现则对非ZIP格式的识别存在限制,因此报错。
直接解决方案:iOS端换用GZIP专用解压逻辑
放弃用SSZipArchive处理GZIP文件,改用iOS原生zlib库实现解压,比如封装NSData的扩展方法:#import <zlib.h> @interface NSData (GZIP) - (NSData *)gzipInflate; @end @implementation NSData (GZIP) - (NSData *)gzipInflate { if ([self length] == 0) return self; unsigned full_length = [self length]; unsigned half_length = [self length] / 2; NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length]; BOOL done = NO; int status; z_stream strm; strm.next_in = (Bytef *)[self bytes]; strm.avail_in = [self length]; strm.total_out = 0; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; if (inflateInit2(&strm, (15+32)) != Z_OK) return nil; while (!done) { 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; status = inflate (&strm, Z_NO_FLUSH); if (status == Z_STREAM_END) done = YES; else if (status != Z_OK) break; } if (inflateEnd (&strm) != Z_OK) return nil; if (done) { [decompressed setLength: strm.total_out]; return decompressed; } else return nil; } @end调用示例:
NSData *gzipData = [NSData dataWithContentsOfURL:downloadedStoreURL]; NSData *uncompressedData = [gzipData gzipInflate]; if (uncompressedData) { [uncompressedData writeToURL:destinationURL atomically:YES]; } else { CLHLog(@"Couldn't unzip downloaded database"); return NO; }备选方案:C#端生成ZIP格式文件
如果坚持要用SSZipArchive,可修改C#代码生成标准ZIP包:using (FileStream zipToOpen = new FileInfo("output.zip").Create()) { using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Create)) { ZipArchiveEntry entry = archive.CreateEntry("InspectionForms_compresed.sqlite"); using (FileStream fileStream = new FileInfo(filePath).OpenRead()) using (Stream entryStream = entry.Open()) { fileStream.CopyTo(entryStream); } } }这样iOS端用现有SSZipArchive代码即可正常解压。
内容的提问来源于stack exchange,提问作者ramdev

