经过深入研究排查,发现目录或文件名编码错误!但是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