iOS SSZipArchive 解压后 中文文件名乱码问题

经过深入研究排查,发现目录或文件名编码错误!但是POD库,不可能直接在里面改!只能进行封装修改!

1、如果是直接拖代码的方式, 改成下面的:

ini 复制代码
 NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
 NSString *strPath = [NSString stringWithCString:filename encoding:enc];

2、如果 pod 'SSZipArchive', 则需要封装filePath:

.h 头文件:

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

.m 实现文件:

ini 复制代码
//
//  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
相关推荐
叽哥4 小时前
Flutter Riverpod上手指南
android·flutter·ios
用户091 天前
SwiftUI Charts 函数绘图完全指南
ios·swiftui·swift
YungFan1 天前
iOS26适配指南之UIColor
ios·swift
权咚2 天前
阿权的开发经验小集
git·ios·xcode
用户092 天前
TipKit与CloudKit同步完全指南
ios·swift
法的空间2 天前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
2501_915918412 天前
iOS 上架全流程指南 iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传 ipa 与审核实战经验分享
android·ios·小程序·uni-app·cocoa·iphone·webview
00后程序员张2 天前
iOS App 混淆与加固对比 源码混淆与ipa文件混淆的区别、iOS代码保护与应用安全场景最佳实践
android·安全·ios·小程序·uni-app·iphone·webview
Magnetic_h3 天前
【iOS】设计模式复习
笔记·学习·ios·设计模式·objective-c·cocoa
00后程序员张3 天前
详细解析苹果iOS应用上架到App Store的完整步骤与指南
android·ios·小程序·https·uni-app·iphone·webview