我的应用程序有一个草稿表视图,显示用户保存的音频文件。对于数据源数组,我循环遍历drafts目录(在NSDocumentDirectory
中)。代码运行良好,它显示了到目前为止我保存的所有文件。
问题是,就在最近,除了前两个文件之外的所有文件都是空的。通过空,我的意思是:当我使用NSData的datatWithContentsOfFile
方法数据长度为0,即0字节。前两个文件仍然有数据,每个大约267639 b。
但是如果文件中没有数据,为什么当我循环遍历草稿目录时它会出现?这些空文件直到最近都完好无损。是什么导致它们变成空的呢?
下面是循环遍历drafts目录的代码。
// check if drafts folders exist
BOOL draftsFoldersExist = NO;
NSError *error = nil;
NSArray *folders = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSArray *appFolderContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[folders objectAtIndex:0] error:&error];
if ([appFolderContents count] > 0 && error == nil) {
for (NSString *item in appFolderContents) {
if ([item isEqualToString:@"drafts"])
draftsFoldersExist = YES;
}
}
_draftsArray = [[NSMutableArray alloc] init];
if (draftsFoldersExist) {
// drafts data source
NSString *draftsPath = [NSString stringWithFormat:@"%@/drafts", [folders objectAtIndex:0]];
NSString *draftsPathEncoded = [draftsPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *draftsPathURL = [NSURL URLWithString:draftsPathEncoded];
// enumerate files in drafts folders
NSArray *desiredProperties = @[NSURLIsReadableKey, NSURLCreationDateKey];
NSArray *drafts = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:draftsPathURL includingPropertiesForKeys:desiredProperties options:0 error:&error];
for (NSURL *item in drafts) {
NSMutableDictionary *draftDict = [[NSMutableDictionary alloc] init];
// name
NSString *name = [item lastPathComponent];
// is readable
NSNumber *isReadableBoolValue = nil;
[item getResourceValue:&isReadableBoolValue forKey:NSURLIsReadableKey error:&error];
if ([isReadableBoolValue isEqualToNumber:@YES]) {
// filename
[draftDict setValue:[name stringByDeletingPathExtension] forKey:@"draftFilename"];
// creation date
NSDate *creationDate = nil;
[item getResourceValue:&creationDate forKey:NSURLCreationDateKey error:&error];
[draftDict setValue:creationDate forKey:@"creationDate"];
// insert into first position of data source array
_draftsArray = [[@[draftDict] arrayByAddingObjectsFromArray:_draftsArray] mutableCopy];
// meta
} else {
NSLog(@"unreadable item: %@", name);
}
}
}
更新:我按照@Joride的建议通过organizer下载了应用目录的文件,发现所有的文件都完好无损,并且有数据。如下:
2014-08-08_20-53-30.m4a
2014-08-09_19-11-08.m4a
2014-08-10_17-36-28.m4a
2014-08-11_18-53-46.m4a
2014-08-13_12-57-57.m4a
2014-08-16_20-44-33.m4a
2014-08-16_20-45-06.m4a
我想现在的问题是为什么他们中的一些人没有用dataWithContentsOfFile
方法显示任何数据。
NSData *fileData = [NSData dataWithContentsOfFile:filepath options:NSDataReadingMapped error:&readingError];
对于零数据的文件,读取错误提示"操作无法完成"。
我找到了问题所在。应用程序的文档目录在某个时候发生了变化,所以文件的位置也发生了变化。
文件保存在这里:
/var/mobile/Applications/09E7C349-6D03-4D2F-BE17-46C00B17C9F5/Documents/drafts/2014-08-13_12-57-57.m4a
和后面这里的
/var/mobile/Applications/1A444A31-C29D-4B0F-8B47-A5B57D7F3281/Documents/drafts/2014-08-16_20-45-06.m4a
注意应用的文件夹有一个不同的令牌。所以文件"在那里",但我猜不是在那里。显然,当使用新的配置文件或应用商店更新时,应用程序的目录可能会发生变化(参见此答案)。
所以这个故事的寓意是:不要将绝对路径保存为文件的引用。
好了,我已经写下了我认为你想要达到的目标。注意,在这个例子中,加载文件的代码是在主队列上同步完成的(在某个viewDidAppear:方法中)。对于实际代码来说,这当然是一个非常糟糕的设计,因为它可能会阻塞UI和主队列。但这应该行得通。如果是这样,那么我认为您的代码在url或加载文件时使用的选项方面存在一些问题。如果这段代码也不能工作,则说明有其他原因导致了问题。
给你:
#import "ViewController.h"
NSString * const kDraftsDir = @"drafts";
@interface ViewController ()
@property (nonatomic, readonly) NSMutableArray * drafts;
@end
@implementation ViewController
@synthesize drafts = _drafts;
- (NSMutableArray *) drafts
{
if (nil == _drafts)
{
_drafts = [[NSMutableArray alloc] init];
}
return _drafts;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear: animated];
[self readDrafts];
}
- (void) readDrafts
{
NSFileManager * fileManager = [[NSFileManager alloc] init];
NSError * error;
NSURL * appDocumentDirectoryURL = [fileManager URLForDirectory: NSDocumentDirectory
inDomain: NSUserDomainMask
appropriateForURL: nil
create: YES
error: &error];
if (nil == appDocumentDirectoryURL)
{
NSLog(@"Error getting appDocumentDirectoryURL: %@, %@", error, [error localizedDescription]);
}
NSURL * draftsDirectoryURL = [appDocumentDirectoryURL URLByAppendingPathComponent: kDraftsDir];
if ([fileManager fileExistsAtPath: draftsDirectoryURL.path isDirectory: NULL])
{
// the folder exists, there might be drafts
NSArray * files = [fileManager contentsOfDirectoryAtURL: draftsDirectoryURL
includingPropertiesForKeys: nil
options: NSDirectoryEnumerationSkipsHiddenFiles
error: &error];
if (nil == files)
{
NSLog(@"Error loading drafts from disk: %@, %@", error, [error localizedDescription]);
}
else
{
for (NSURL * aDraftURL in files)
{
// This is just a check for debugging purposes
NSData * draftData = [NSData dataWithContentsOfURL: aDraftURL];
if (draftData.length == 0)
{
NSLog(@"WARNING: no file, or zero-size file at URL: %@", aDraftURL);
}
NSMutableDictionary * draftDict = [[NSMutableDictionary alloc] init];
NSString * draftName = [[aDraftURL lastPathComponent]stringByDeletingPathExtension];
// let's make sure we won't crash because we are inserting nil into a dictionary
if (nil != draftName)
{
draftDict[@"draftFilename"] = [[aDraftURL lastPathComponent]stringByDeletingPathExtension];
}
else
{
NSLog(@"WARNING: no fileName without extension for draft at URL: %@", aDraftURL);
}
NSDate * creationDate = nil;
[aDraftURL getResourceValue: &creationDate forKey:NSURLCreationDateKey error:&error];
// let's make sure we won't crash because we are inserting nil into a dictionary
if (nil != creationDate)
{
draftDict[@"creationDate"] = creationDate;
}
else
{
NSLog(@"WARNING: no creationdate found for file at URL: %@", aDraftURL);
}
// self.drafts will always return a fully initialized NSMutableArray, so we
// can safely add an object at index 0
[self.drafts insertObject: draftDict atIndex: 0];
}
}
}
else
{
NSLog(@"The drafts folder does not exist.");
}
NSLog(@"%@ stored drafts: %@", @([self.drafts count]), self.drafts);
}
@end