Files in iOS

1. Finding the path of your file

a. You may have added file into your project.

Note: two ways to find the path

NSString *strFilePath = [[NSBundle mainBundle] PathForResource:@"fileName" ofType:@"extention"];
NSString *strFilePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"filename.txt"];

b. Your file in the App file like /Documents.

//This gives the /Documents path of an app, the 1st thing comes out in the array is the path string
NSArray *arryDocumentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *strDocumentPath = [arryDocumentPaths objectAtIndex: 0];
//In short we have.把以上两行缩写起来
NSString *strDocumentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex: 0];
//finally, we can append the "filename.txt"
NSString *strFilePath = [strDocumentPath stringByAppendingPathComponent:@"filename.txt"];
//其实 一行就够了
NSString *strFilePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex: 0] stringByAppendingPathComponent:@"filename.txt"];

//另外一种方法获得document path
[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];

c. A little bit about property list (xxx.plist)

//这个东西可以用来作为用户设置,程序启动的时候先读取设置,在按照用户习惯加载相关设置。
NSString *resourcesDir = [[NSBundle mainBundle] resourcePath];
NSString *sourcePath = [resourcesDir stringByAppendingPathComponent:@"xxx.plist"];
NSDictionary *content = [[NSDictionary alloc] initWithContentsOfFile:sourcePath];

2. Creating/Editing a file in the path you gave

a. Before you creating any file, you may want to check if the file already exists. In that case, NSFileManager is here to help.

//check if file exists in doc dir[Yufei Lang 3/6/2012]
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isFileExist = [fileManager fileExistsAtPath:filePath];
if (!isFileExist)//if not, create one.
{
//asume we have a NSArray which full of strings and it called arryFullOfStrings.
[arryFullOfStrings writeToFile:strFilePath atomically:NO];//strFilePath is the string we got earlier. like ".../.../file.txt"
}

b. if you want to read or modify a file that exists. you do this.

//接上面的代码
else//if exists, read it, modify it, save it again.[Yufei Lang 3/6/2012]
        {
            NSMutableArray *arryRecordInFile = [NSMutableArray arrayWithContentsOfFile:filePath];
            //if current key word exists in the file, ignore it. Means you may not have to modify it at all[Yufei Lang 3/6/2012]
            if(![arryRecordInFile containsObject:strKeyWordYouWantToSearch])//if it doesn't exist.
            {
                [arryRecordInFile addObject:strNewKeyWordYouWantToAdd];//you can add any object other than a string
                [arryRecordInFile writeToFile:filePath atomically:NO];//write the array back to file
            }
            else//you may give a warning if the key work exists
            {
                UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:@"Title" message:@"key word exists" delegate:nil cancelButtonTitle:@"Okay." otherButtonTitles:nil];
                [myAlert show];
                [myAlert release];
            }
        }
note: here is how to fill a NSData from a file
NSData *data = [[NSData alloc] initWithContentsOfFile:strFilePath];

c. A little bit more about NSFileManager

note: this is how to get all file names from given path.
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error;
NSArray *arryFileList = [fm contentsOfDirectoryAtPath:strGivenDirPath error:&error];
note: here is how to get attributes of file from given path.
NSDictionary *dictFilesAttributes = [fm attributesOfItemAtPath:strGivenFilePath error:&error];

3. Copy resource files/folders (from Bundle) to app sand box (app files)

如果你有一个数据表,它本应该是当用户启动程序后从网络上下载下来的,但是却要花费很长时间,而且有可能占用用户的3G网络,可是你又不能把它放在resource里面,因为这些数据有可能被更新(这类数据通常在程序加载后被下载到app/Documents文件夹下)。那么一个解决办法就是: 在程序启动后检查Documents下面有没有这个文件,如果没有就将resource里的文件拷贝过去,在检查这个文件的版本,并适当更新其中的内容。

那接下来一段代码就是如何将resource里的文件/目录拷贝到documents里(deep copy)。

首先,如果这是一个文件夹,你要将它添加到工程里。添加的时候要注意,不要选取分组group目录,而要选取“creating folder references…”

添加之后,你会发现这些文件会变成蓝色的小文件夹显示在你的工程里,而不是平时一样的黄色文件夹。

接下来,我要把这个“Documents”文件夹下的所有文件copy到app/Documents下,注意这个一个deep copy,也就是说所有子文件夹下的所有文件及子文件夹都将被拷贝。

- (void)fillDocumentFolderWithFilesInResource
{
    NSFileManager *defaultFileMgr = [NSFileManager defaultManager];
    NSError *error = nil;
    NSString *preDownloadFolderName = @"Documents";
    NSString *bundlePathString = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:preDownloadFolderName];
    NSURL *bundleURL = [[NSURL alloc] initFileURLWithPath:bundlePathString isDirectory:YES];
    
    NSArray *keys = [NSArray arrayWithObjects: NSURLIsRegularFileKey, NSURLIsDirectoryKey, nil];
    
    NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager]
                                             enumeratorAtURL:bundleURL
                                             includingPropertiesForKeys:keys
                                             options:(NSDirectoryEnumerationSkipsHiddenFiles)
                                             errorHandler:^(NSURL *url, NSError *error) {
                                                 NSLog(@"%@", error.description);
                                                 return YES;
                                             }];
    
    NSString *docPathString =
    [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSURL *docURL = [[NSURL alloc] initFileURLWithPath:docPathString isDirectory:YES];
    
    NSMutableArray *fileURLs = [NSMutableArray array];
    
    // create folders [yufei]
    for (NSURL *url in fileEnumerator)
    {
        NSNumber *isDirectory = nil;
        [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
        
        if ([isDirectory boolValue])
        {
            NSString *subPath = [[url.path componentsSeparatedByString:[preDownloadFolderName stringByAppendingFormat:@"/"]] lastObject];
            NSURL *destURL = [docURL URLByAppendingPathComponent:subPath];
            if (![defaultFileMgr createDirectoryAtPath:destURL.path withIntermediateDirectories:YES attributes:nil error:&error])
                NSLog(@"[error]: faild creating directory [%@]. Reason: %@", subPath, error.description);
        }
        
        NSNumber *isRegularFile = nil;
        [url getResourceValue:&isRegularFile forKey:NSURLIsRegularFileKey error:nil];
        
        if ([isRegularFile boolValue])
            [fileURLs addObject:url];
    }
    
    // copy all files [yufei]
    for (NSURL *urlInEnum in fileURLs)
    {
        NSString *subPath = [[urlInEnum.path componentsSeparatedByString:[preDownloadFolderName stringByAppendingFormat:@"/"]] lastObject];
        NSURL *destURL = [docURL URLByAppendingPathComponent:subPath];
        
        if (![defaultFileMgr copyItemAtURL:urlInEnum toURL:destURL error:nil])
            NSLog(@"[error]: copy file [%@] faild.", subPath);
    }
    
    [bundleURL release];
}

更新:
发现这种办法只在iOS4以上适用,没办法为了支持3.x的系统只好重新写一个方法,大同小异.

- (void)fillSubfolderPathesTo:(NSMutableArray *)folderPathArray regularFilePathesTo:(NSMutableArray *)filePathArray atPath:(NSString *)pathString
{
    NSFileManager *defaultFileMgr = [NSFileManager defaultManager];
    NSError *error = nil;
    NSArray *contents = [defaultFileMgr contentsOfDirectoryAtPath:pathString error:&error];
    if (contents == nil)
        NSLog(@"[error]: faild retrieving contents from directory [%@]. Reason: %@", pathString, error.description);
    else
    {
        for (NSString *contentsStr in contents)
        {
            NSString *subContectPath = [pathString stringByAppendingPathComponent:contentsStr];
            NSDictionary *attuibutesDic = [defaultFileMgr attributesOfItemAtPath:subContectPath error:&error];
            if (attuibutesDic == nil)
                NSLog(@"[error]: faild getting attributes from path [%@]. Reason: %@", subContectPath, error.description);
            else
            {
                NSString *fileType = [attuibutesDic valueForKey:NSFileType];
                if ([NSFileTypeRegular isEqualToString:fileType])
                {
                    [filePathArray addObject:subContectPath];
                }
                else if ([NSFileTypeDirectory isEqualToString:fileType])
                {
                    [folderPathArray addObject:subContectPath];
                    [self fillSubfolderPathesTo:folderPathArray regularFilePathesTo:filePathArray atPath:subContectPath];
                }
            }
        }
    }
}

- (void)checkAndFillDocumentFolderWithPreDownloadFiles
{
    NSFileManager *defaultFileMgr = [NSFileManager defaultManager];
    
    NSString *docPathString = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    
    if ([defaultFileMgr fileExistsAtPath:[docPathString stringByAppendingPathComponent:@"dataStores.sqlite"]])
        return;
    else
    {
        NSError *error = nil;
        NSString *preDownloadFolderName = @"Documents";//Documents
        NSString *bundlePathString = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:preDownloadFolderName];
        
        NSMutableArray *folderPathArray = [[NSMutableArray alloc] init];
        NSMutableArray *filePathArray = [[NSMutableArray alloc] init];
        
        [self fillSubfolderPathesTo:folderPathArray regularFilePathesTo:filePathArray atPath:bundlePathString];
        
        for (NSString *folderPathString in folderPathArray)
        {
            NSString *subPath = [[folderPathString componentsSeparatedByString:[preDownloadFolderName stringByAppendingFormat:@"/"]] lastObject];
            NSString *destPath = [docPathString stringByAppendingPathComponent:subPath];
            if (![defaultFileMgr createDirectoryAtPath:destPath withIntermediateDirectories:YES attributes:nil error:&error])
                NSLog(@"[error]: faild creating directory [%@]. Reason: %@", destPath, error.description);
            
        }
        
        for (NSString *filePathString in filePathArray)
        {
            NSString *subPath = [[filePathString componentsSeparatedByString:[preDownloadFolderName stringByAppendingFormat:@"/"]] lastObject];
            NSString *destPath = [docPathString stringByAppendingPathComponent:subPath];
            if (![defaultFileMgr copyItemAtPath:filePathString toPath:destPath error:&error])
                NSLog(@"[error]: faild copy file to [%@].", destPath);
        }
        
        [folderPathArray release];
        [filePathArray release];
    }    
}

(click me)一篇关于NSBundle的文章 转自新浪博客

《Files in iOS》有1个想法

发表评论

电子邮件地址不会被公开。 必填项已用*标注