不知道什么情况,做为一个三方广泛使用的框架库,会出现这种比较低级的问题!
还有中文的文件名解压后显示乱码!
经过深入研究排查,发现目录或文件名编码错误!但是POD库,不可能直接在里面改!只能进行封装修改!
修复后的显示:
修复文件代码:头文件
objectivec// // ZipRar7zUnArchive.h // ZipRar7zUnArchive // // Created by Saravanan V on 26/04/13. // Copyright (c) 2013 SARAVANAN. All rights reserved. // #import <Foundation/Foundation.h> #import <SSZipArchive/SSZipArchive.h> #define UNIQUE_KEY( x ) NSString * const x = @#x enum{ SARFileTypeZIP, SARFileTypeRAR }; static UNIQUE_KEY( rar ); static UNIQUE_KEY( zip ); typedef void(^completionBlock)(BOOL result); @interface ZipRar7zUnArchive : NSObject{ } + (BOOL)isPasswordProtectedAtPath:(NSString *)path; + (BOOL)unarchiveFrom:(NSString *)filePath toPath:(NSString *)topath password:(NSString *)password; @end
实现逻辑:
objectivec// // ZipRar7zUnArchive.m // ZipRar7zUnArchive // // Created by Saravanan V on 26/04/13. // Copyright (c) 2013 SARAVANAN. All rights reserved. // #import "ZipRar7zUnArchive.h" #import <UnrarKit/UnrarKit.h> #import "LzmaSDKObjC.h" #import "SSZipCommon.h" #import "mz_compat.h" #import "mz_zip.h" #include <zlib.h> #include <sys/stat.h> @interface ZipRar7zUnArchive ()<SSZipArchiveDelegate>{ } @property (nonatomic, strong) SSZipArchive *zipArchive; @property (nonatomic, strong) NSString *filePath; @property (nonatomic, strong) NSString *password; @property (nonatomic, strong) NSString *destinationPath; @end @implementation ZipRar7zUnArchive + (ZipRar7zUnArchive *)instance { static dispatch_once_t onceToken; static ZipRar7zUnArchive *m = nil; dispatch_once(&onceToken, ^{ m = [[ZipRar7zUnArchive alloc] init]; }); return m; } #pragma mark - Init Methods + (BOOL)unarchiveFrom:(NSString *)filePath toPath:(NSString *)topath password:(NSString *)password { ZipRar7zUnArchive *a = [ZipRar7zUnArchive instance]; [a setFilePath:filePath]; [a setDestinationPath:topath]; [a setPassword:password]; return [a decompress]; } + (BOOL)isPasswordProtectedAtPath:(NSString *)path { NSError *archiveError = nil; //URKArchive用来判断是否要输入密码,filePath是要解压的文件路径 URKArchive *archive = [[URKArchive alloc] initWithPath:path error:&archiveError]; if ([archive isPasswordProtected]) { return YES; } return NO; } #pragma mark - Helper Methods - (NSString *)fileType{ NSArray *derivedPathArr = [_filePath componentsSeparatedByString:@"/"]; NSString *lastObject = [derivedPathArr lastObject]; NSString *fileType = [[lastObject componentsSeparatedByString:@"."] lastObject]; return [fileType lowercaseString]; } #pragma mark - Decompressing Methods - (BOOL)decompress{ // NSLog(@"_fileType : %@",_fileType); if ( [[self fileType] compare:rar options:NSCaseInsensitiveSearch] == NSOrderedSame ) { return [self rarDecompress]; } else if ( [[self fileType] compare:zip options:NSCaseInsensitiveSearch] == NSOrderedSame ) { return [self zipDecompress]; } else if ( [[self fileType] compare:@"7z" options:NSCaseInsensitiveSearch] == NSOrderedSame ) { return [self decompress7z]; } return NO; } - (BOOL)rarDecompress { // _filePath = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"rar"]; NSLog(@"filePath : %@",_filePath); NSLog(@"destinationPath : %@",_destinationPath); NSError *archiveError = nil; URKArchive *archive = [[URKArchive alloc] initWithPath:_filePath error:&archiveError]; if (!archive) { NSLog(@"Failed!"); return NO; } NSError *error = nil; NSArray *filenames = [archive listFilenames:&error]; if (archive.isPasswordProtected) { archive.password = self.password; } if (error) { NSLog(@"Error reading archive: %@", error); return NO; } // for (NSString *filename in filenames) { // NSLog(@"File: %@", filename); // } // Extract a file into memory just to validate if it works/extracts [archive extractDataFromFile:filenames[0] progress:nil error:&error]; if (error) { if (error.code == ERAR_MISSING_PASSWORD) { NSLog(@"Password protected archive! Please provide a password for the archived file."); } return NO; } else { return true; } } - (BOOL)zipDecompress{ /*BOOL unzipped = [SSZipArchive unzipFileAtPath:_filePath toDestination:_destinationPath delegate:self];*/ BOOL unzipped = [SSZipArchive unzipFileAtPath:self.filePath toDestination:self.destinationPath fileName:[self.destinationPath lastPathComponent] preserveAttributes:YES overwrite:YES nestedZipLevel:0 password:nil error:nil delegate:self progressHandler:nil completionHandler:nil];; // NSLog(@"unzipped : %d",unzipped); NSError *error; if (self.password != nil && self.password.length > 0) { // unzipped = [SSZipArchive unzipFileAtPath:_filePath toDestination:_destinationPath overwrite:NO password:self.password error:&error delegate:self]; // unzipped = [SSZipArchive unzipFileAtPath:_filePath toDestination:_destinationPath fileName:@"" preserveAttributes:YES overwrite:NO nestedZipLevel:0 password:self.password error:error delegate:self progressHandler:nil completionHandler:nil]; NSLog(@"error: %@", error); } return unzipped; } - (BOOL)decompress7z{ NSLog(@"_filePath: %@", _filePath); NSLog(@"_destinationPath: %@", _destinationPath); // LzmaSDKObjCReader *reader = [[LzmaSDKObjCReader alloc] initWithFileURL:[NSURL fileURLWithPath:_filePath]]; // 1.2 Or create with predefined archive type if path doesn't containes suitable extension LzmaSDKObjCReader *reader = [[LzmaSDKObjCReader alloc] initWithFileURL:[NSURL fileURLWithPath:_filePath] andType:LzmaSDKObjCFileType7z]; // // Optionaly: assign weak delegate for tracking extract progress. // reader.delegate = self; // If achive encrypted - define password getter handler. // NOTES: // - Encrypted file needs password for extract process. // - Encrypted file with encrypted header needs password for list(iterate) and extract archive items. reader.passwordGetter = ^NSString*(void){ // return @"password to my achive"; NSLog(@"self.password: %@", self.password); return self.password; }; // Open archive, with or without error. Error can be nil. NSError * error = nil; if (![reader open:&error]) { NSLog(@"Open error: %@", error); } NSLog(@"Open error: %@", reader.lastError); NSMutableArray *filePathsArray = [NSMutableArray array]; NSMutableArray * items = [NSMutableArray array]; // Array with selected items. // Iterate all archive items, track what items do you need & hold them in array. [reader iterateWithHandler:^BOOL(LzmaSDKObjCItem * item, NSError * error){ // NSLog(@"\nitem:%@", item); if (item) { [items addObject:item]; // if needs this item - store to array. if (!item.isDirectory) { NSString *filePath = [_destinationPath stringByAppendingPathComponent:item.directoryPath]; filePath = [filePath stringByAppendingPathComponent:item.fileName]; [filePathsArray addObject:filePath]; } } return YES; // YES - continue iterate, NO - stop iteration }]; NSLog(@"Iteration error: %@", reader.lastError); // Extract selected items from prev. step. // YES - create subfolders structure for the items. // NO - place item file to the root of path(in this case items with the same names will be overwrited automaticaly). [reader extract:items toPath:_destinationPath withFullPaths:YES]; NSLog(@"Extract error: %@", reader.lastError); // Test selected items from prev. step. [reader test:items]; NSLog(@"test error: %@", reader.lastError); if (reader.lastError || ![filePathsArray count]) { return NO; } else { return YES; } } #pragma mark - Utility Methods - (NSString *) applicationDocumentsDirectory { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; return basePath; } @end BOOL _afileIsSymbolicLink(const unz_file_info *fileInfo) { // // Determine whether this is a symbolic link: // - File is stored with 'version made by' value of UNIX (3), // as per https://www.pkware.com/documents/casestudies/APPNOTE.TXT // in the upper byte of the version field. // - BSD4.4 st_mode constants are stored in the high 16 bits of the // external file attributes (defacto standard, verified against libarchive) // // The original constants can be found here: // https://minnie.tuhs.org/cgi-bin/utree.pl?file=4.4BSD/usr/include/sys/stat.h // const uLong ZipUNIXVersion = 3; const uLong BSD_SFMT = 0170000; const uLong BSD_IFLNK = 0120000; BOOL fileIsSymbolicLink = ((fileInfo->version >> 8) == ZipUNIXVersion) && BSD_IFLNK == (BSD_SFMT & (fileInfo->external_fa >> 16)); return fileIsSymbolicLink; } @interface SSZipArchive (extra) + (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination fileName:(NSString *)fileName preserveAttributes:(BOOL)preserveAttributes overwrite:(BOOL)overwrite nestedZipLevel:(NSInteger)nestedZipLevel password:(nullable NSString *)password error:(NSError **)error delegate:(nullable id<SSZipArchiveDelegate>)delegate progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler; + (NSString *)filenameStringWithCString:(const char *)filename version_made_by:(uint16_t)version_made_by general_purpose_flag:(uint16_t)flag size:(uint16_t)size_filename; @end @implementation SSZipArchive (extra) + (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination fileName:(NSString *)fileName preserveAttributes:(BOOL)preserveAttributes overwrite:(BOOL)overwrite nestedZipLevel:(NSInteger)nestedZipLevel password:(nullable NSString *)password error:(NSError **)error delegate:(nullable id<SSZipArchiveDelegate>)delegate progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler { // Guard against empty strings if (path.length == 0 || destination.length == 0) { NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"received invalid argument(s)"}; NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeInvalidArguments userInfo:userInfo]; if (error) { *error = err; } if (completionHandler) { completionHandler(nil, NO, err); } return NO; } // Begin opening zipFile zip = unzOpen(path.fileSystemRepresentation); if (zip == NULL) { NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"failed to open zip file"}; NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFailedOpenZipFile userInfo:userInfo]; if (error) { *error = err; } if (completionHandler) { completionHandler(nil, NO, err); } return NO; } NSDictionary * fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil]; unsigned long long fileSize = [[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue]; unsigned long long currentPosition = 0; unz_global_info globalInfo = {}; unzGetGlobalInfo(zip, &globalInfo); // Begin unzipping int ret = 0; ret = unzGoToFirstFile(zip); if (ret != UNZ_OK && ret != MZ_END_OF_LIST) { NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"failed to open first file in zip file"}; NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFailedOpenFileInZip userInfo:userInfo]; if (error) { *error = err; } if (completionHandler) { completionHandler(nil, NO, err); } unzClose(zip); return NO; } BOOL success = YES; BOOL canceled = NO; int crc_ret = 0; unsigned char buffer[4096] = {0}; NSFileManager *fileManager = [NSFileManager defaultManager]; NSMutableArray<NSDictionary *> *directoriesModificationDates = [[NSMutableArray alloc] init]; // Message delegate if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipArchiveAtPath:zipInfo:)]) { [delegate zipArchiveWillUnzipArchiveAtPath:path zipInfo:globalInfo]; } if ([delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) { [delegate zipArchiveProgressEvent:currentPosition total:fileSize]; } NSInteger currentFileNumber = -1; NSError *unzippingError; do { currentFileNumber++; if (ret == MZ_END_OF_LIST) { break; } @autoreleasepool { if (password.length == 0) { ret = unzOpenCurrentFile(zip); } else { ret = unzOpenCurrentFilePassword(zip, [password cStringUsingEncoding:NSUTF8StringEncoding]); } if (ret != UNZ_OK) { unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFailedOpenFileInZip userInfo:@{NSLocalizedDescriptionKey: @"failed to open file in zip file"}]; success = NO; break; } // Reading data and write to file unz_file_info fileInfo; memset(&fileInfo, 0, sizeof(unz_file_info)); ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0); if (ret != UNZ_OK) { unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFileInfoNotLoadable userInfo:@{NSLocalizedDescriptionKey: @"failed to retrieve info for file"}]; success = NO; unzCloseCurrentFile(zip); break; } currentPosition += fileInfo.compressed_size; // Message delegate if ([delegate respondsToSelector:@selector(zipArchiveShouldUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) { if (![delegate zipArchiveShouldUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry archivePath:path fileInfo:fileInfo]) { success = NO; canceled = YES; break; } } if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) { [delegate zipArchiveWillUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry archivePath:path fileInfo:fileInfo]; } if ([delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) { [delegate zipArchiveProgressEvent:(NSInteger)currentPosition total:(NSInteger)fileSize]; } char *filename = (char *)malloc(fileInfo.size_filename + 1); if (filename == NULL) { success = NO; break; } unzGetCurrentFileInfo(zip, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0); filename[fileInfo.size_filename] = '\0'; BOOL fileIsSymbolicLink = _afileIsSymbolicLink(&fileInfo); const char *cname = [fileName cStringUsingEncoding:NSUTF8StringEncoding]; NSString * strPath = [SSZipArchive filenameStringWithCString:filename version_made_by:fileInfo.version general_purpose_flag:fileInfo.flag size:fileInfo.size_filename]; if ([strPath hasPrefix:@"__MACOSX/"]) { // ignoring resource forks: https://superuser.com/questions/104500/what-is-macosx-folder unzCloseCurrentFile(zip); ret = unzGoToNextFile(zip); // free(filename); continue; } // Check if it contains directory BOOL isDirectory = NO; if (filename[fileInfo.size_filename-1] == '/' || filename[fileInfo.size_filename-1] == '\\') { isDirectory = YES; } free(filename); // Sanitize paths in the file name. strPath = [strPath _sanitizedPath]; if (!strPath.length) { // if filename data is unsalvageable, we default to currentFileNumber strPath = @(currentFileNumber).stringValue; } NSString *fullPath = [destination stringByAppendingPathComponent:strPath]; NSError *err = nil; NSDictionary *directoryAttr; if (preserveAttributes) { NSDate *modDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.mz_dos_date]; directoryAttr = @{NSFileCreationDate: modDate, NSFileModificationDate: modDate}; [directoriesModificationDates addObject: @{@"path": fullPath, @"modDate": modDate}]; } if (isDirectory) { [fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:directoryAttr error:&err]; } else { [fileManager createDirectoryAtPath:fullPath.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:directoryAttr error:&err]; } if (err != nil) { if ([err.domain isEqualToString:NSCocoaErrorDomain] && err.code == 640) { unzippingError = err; unzCloseCurrentFile(zip); success = NO; break; } NSLog(@"[SSZipArchive] Error: %@", err.localizedDescription); } if ([fileManager fileExistsAtPath:fullPath] && !isDirectory && !overwrite) { //FIXME: couldBe CRC Check? unzCloseCurrentFile(zip); ret = unzGoToNextFile(zip); continue; } if (isDirectory && !fileIsSymbolicLink) { // nothing to read/write for a directory } else if (!fileIsSymbolicLink) { // ensure we are not creating stale file entries int readBytes = unzReadCurrentFile(zip, buffer, 4096); if (readBytes >= 0) { FILE *fp = fopen(fullPath.fileSystemRepresentation, "wb"); while (fp) { if (readBytes > 0) { if (0 == fwrite(buffer, readBytes, 1, fp)) { if (ferror(fp)) { NSString *message = [NSString stringWithFormat:@"Failed to write file (check your free space)"]; NSLog(@"[SSZipArchive] %@", message); success = NO; unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFailedToWriteFile userInfo:@{NSLocalizedDescriptionKey: message}]; break; } } } else { break; } readBytes = unzReadCurrentFile(zip, buffer, 4096); if (readBytes < 0) { // Let's assume error Z_DATA_ERROR is caused by an invalid password // Let's assume other errors are caused by Content Not Readable success = NO; } } if (fp) { fclose(fp); if (nestedZipLevel && [fullPath.pathExtension.lowercaseString isEqualToString:@"zip"] && [self unzipFileAtPath:fullPath toDestination:fullPath.stringByDeletingLastPathComponent preserveAttributes:preserveAttributes overwrite:overwrite nestedZipLevel:nestedZipLevel - 1 password:password error:nil delegate:nil progressHandler:nil completionHandler:nil]) { [directoriesModificationDates removeLastObject]; [[NSFileManager defaultManager] removeItemAtPath:fullPath error:nil]; } else if (preserveAttributes) { // Set the original datetime property if (fileInfo.mz_dos_date != 0) { NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.mz_dos_date]; NSDictionary *attr = @{NSFileModificationDate: orgDate}; if (attr) { if (![fileManager setAttributes:attr ofItemAtPath:fullPath error:nil]) { // Can't set attributes NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting modification date"); } } } // Set the original permissions on the file (+read/write to solve #293) uLong permissions = fileInfo.external_fa >> 16 | 0b110000000; if (permissions != 0) { // Store it into a NSNumber NSNumber *permissionsValue = @(permissions); // Retrieve any existing attributes NSMutableDictionary *attrs = [[NSMutableDictionary alloc] initWithDictionary:[fileManager attributesOfItemAtPath:fullPath error:nil]]; // Set the value in the attributes dict [attrs setObject:permissionsValue forKey:NSFilePosixPermissions]; // Update attributes if (![fileManager setAttributes:attrs ofItemAtPath:fullPath error:nil]) { // Unable to set the permissions attribute NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting permissions"); } } } } else { // if we couldn't open file descriptor we can validate global errno to see the reason int errnoSave = errno; BOOL isSeriousError = NO; switch (errnoSave) { case EISDIR: // Is a directory // assumed case break; case ENOSPC: case EMFILE: // No space left on device // or // Too many open files isSeriousError = YES; break; default: // ignore case // Just log the error { NSError *errorObject = [NSError errorWithDomain:NSPOSIXErrorDomain code:errnoSave userInfo:nil]; NSLog(@"[SSZipArchive] Failed to open file on unzipping.(%@)", errorObject); } break; } if (isSeriousError) { // serious case unzippingError = [NSError errorWithDomain:NSPOSIXErrorDomain code:errnoSave userInfo:nil]; unzCloseCurrentFile(zip); // Log the error NSLog(@"[SSZipArchive] Failed to open file on unzipping.(%@)", unzippingError); // Break unzipping success = NO; break; } } } else { // Let's assume error Z_DATA_ERROR is caused by an invalid password // Let's assume other errors are caused by Content Not Readable success = NO; break; } } else { // Assemble the path for the symbolic link NSMutableString *destinationPath = [NSMutableString string]; int bytesRead = 0; while ((bytesRead = unzReadCurrentFile(zip, buffer, 4096)) > 0) { buffer[bytesRead] = 0; [destinationPath appendString:@((const char *)buffer)]; } if (bytesRead < 0) { // Let's assume error Z_DATA_ERROR is caused by an invalid password // Let's assume other errors are caused by Content Not Readable success = NO; break; } // Check if the symlink exists and delete it if we're overwriting if (overwrite) { if ([fileManager fileExistsAtPath:fullPath]) { NSError *localError = nil; BOOL removeSuccess = [fileManager removeItemAtPath:fullPath error:&localError]; if (!removeSuccess) { NSString *message = [NSString stringWithFormat:@"Failed to delete existing symbolic link at \"%@\"", localError.localizedDescription]; NSLog(@"[SSZipArchive] %@", message); success = NO; unzippingError = [NSError errorWithDomain:SSZipArchiveErrorDomain code:localError.code userInfo:@{NSLocalizedDescriptionKey: message}]; } } } // Create the symbolic link (making sure it stays relative if it was relative before) int symlinkError = symlink([destinationPath cStringUsingEncoding:NSUTF8StringEncoding], [fullPath cStringUsingEncoding:NSUTF8StringEncoding]); if (symlinkError != 0) { // Bubble the error up to the completion handler NSString *message = [NSString stringWithFormat:@"Failed to create symbolic link at \"%@\" to \"%@\" - symlink() error code: %d", fullPath, destinationPath, errno]; NSLog(@"[SSZipArchive] %@", message); success = NO; unzippingError = [NSError errorWithDomain:NSPOSIXErrorDomain code:symlinkError userInfo:@{NSLocalizedDescriptionKey: message}]; } } crc_ret = unzCloseCurrentFile(zip); if (crc_ret == MZ_CRC_ERROR) { // CRC ERROR success = NO; break; } ret = unzGoToNextFile(zip); // Message delegate if ([delegate respondsToSelector:@selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) { [delegate zipArchiveDidUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry archivePath:path fileInfo:fileInfo]; } else if ([delegate respondsToSelector: @selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:unzippedFilePath:)]) { [delegate zipArchiveDidUnzipFileAtIndex: currentFileNumber totalFiles: (NSInteger)globalInfo.number_entry archivePath:path unzippedFilePath: fullPath]; } if (progressHandler) { progressHandler(strPath, fileInfo, currentFileNumber, globalInfo.number_entry); } } } while (ret == UNZ_OK && success); // Close unzClose(zip); // The process of decompressing the .zip archive causes the modification times on the folders // to be set to the present time. So, when we are done, they need to be explicitly set. // set the modification date on all of the directories. if (success && preserveAttributes) { NSError * err = nil; for (NSDictionary * d in directoriesModificationDates) { if (![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate: [d objectForKey:@"modDate"]} ofItemAtPath:[d objectForKey:@"path"] error:&err]) { NSLog(@"[SSZipArchive] Set attributes failed for directory: %@.", [d objectForKey:@"path"]); } if (err) { NSLog(@"[SSZipArchive] Error setting directory file modification date attribute: %@", err.localizedDescription); } } } // Message delegate if (success && [delegate respondsToSelector:@selector(zipArchiveDidUnzipArchiveAtPath:zipInfo:unzippedPath:)]) { [delegate zipArchiveDidUnzipArchiveAtPath:path zipInfo:globalInfo unzippedPath:destination]; } // final progress event = 100% if (!canceled && [delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) { [delegate zipArchiveProgressEvent:fileSize total:fileSize]; } NSError *retErr = nil; if (crc_ret == MZ_CRC_ERROR) { NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"crc check failed for file"}; retErr = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFileInfoNotLoadable userInfo:userInfo]; } if (error) { if (unzippingError) { *error = unzippingError; } else { *error = retErr; } } if (completionHandler) { if (unzippingError) { completionHandler(path, success, unzippingError); } else { completionHandler(path, success, retErr); } } return success; } + (NSString *)filenameStringWithCString:(const char *)filename version_made_by:(uint16_t)version_made_by general_purpose_flag:(uint16_t)flag size:(uint16_t)size_filename { // Respect Language encoding flag only reading filename as UTF-8 when this is set // when file entry created on dos system. // // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT // Bit 11: Language encoding flag (EFS). If this bit is set, // the filename and comment fields for this file // MUST be encoded using UTF-8. (see APPENDIX D) uint16_t made_by = version_made_by >> 8; BOOL made_on_dos = made_by == 0; BOOL languageEncoding = (flag & (1 << 11)) != 0; if (!languageEncoding && made_on_dos) { // APPNOTE.TXT D.1: // D.2 If general purpose bit 11 is unset, the file name and comment should conform // to the original ZIP character encoding. If general purpose bit 11 is set, the // filename and comment must support The Unicode Standard, Version 4.1.0 or // greater using the character encoding form defined by the UTF-8 storage // specification. The Unicode Standard is published by the The Unicode // Consortium (www.unicode.org). UTF-8 encoded data stored within ZIP files // is expected to not include a byte order mark (BOM). // Code Page 437 corresponds to kCFStringEncodingDOSLatinUS NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingDOSChineseSimplif); NSString* strPath = [NSString stringWithCString:filename encoding:encoding]; if (strPath) { return strPath; } } // attempting unicode encoding NSString * strPath = @(filename); if (strPath) { return strPath; } // if filename is non-unicode, detect and transform Encoding NSData *data = [NSData dataWithBytes:(const void *)filename length:sizeof(unsigned char) * size_filename]; // Testing availability of @available (https://stackoverflow.com/a/46927445/1033581) #if __clang_major__ < 9 // Xcode 8- if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_9_2) { #else // Xcode 9+ if (@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)) { #endif // supported encodings are in [NSString availableStringEncodings] [NSString stringEncodingForData:data encodingOptions:nil convertedString:&strPath usedLossyConversion:nil]; } else { // fallback to a simple manual detect for macOS 10.9 or older NSArray<NSNumber *> *encodings = @[@(kCFStringEncodingGB_18030_2000), @(kCFStringEncodingShiftJIS)]; for (NSNumber *encoding in encodings) { strPath = [NSString stringWithCString:filename encoding:(NSStringEncoding)CFStringConvertEncodingToNSStringEncoding(encoding.unsignedIntValue)]; if (strPath) { break; } } } if (strPath) { return strPath; } // if filename encoding is non-detected, we default to something based on data // _hexString is more readable than _base64RFC4648 for debugging unknown encodings strPath = [data _hexString]; return strPath; } @end