我正在研究支持iTunes文件共享功能的iOS项目。目标是实时跟踪传入/更改的数据。
我正在使用Apple的示例代码中的(有点修改) DirectoryWatcher类 并尝试了this source code。
数据是NSBundle(* .bundle),一些捆绑包在100-500 MB范围内,取决于其内容,一些视频/音频内容。包中有基于xml的描述符文件。
问题是上面触发通知上面的任何一个代码,或者刚开始复制时的其他任何代码,但复制/更改/删除过程完全完成时。
接下来尝试:
检查文件属性:
NSDictionary *fileAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:[contURL path] error:nil];
BOOL fileBusy = [[fileAttrs objectForKey:NSFileBusy] boolValue];
寻找fileSize
更改:
dispatch_async(_checkQueue, ^{
for (NSURL *contURL in tempBundleURLs) {
NSInteger lastSize = 0;
NSDictionary *fileAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:[contURL path] error:nil];
NSInteger fileSize = [[fileAttrs objectForKey:NSFileSize] intValue];
do {
lastSize = fileSize;
[NSThread sleepForTimeInterval:1];
fileAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:[contURL path] error:nil];
fileSize = [[fileAttrs objectForKey:NSFileSize] intValue];
NSLog(@"doing job");
} while (lastSize != fileSize);
NSLog(@"next job");
}
);
任何其他解决方案?
上面的解决方案适用于bin文件,但不适用于.bundle(因为.bundle文件实际上是目录)。为了使它与.bundle一起使用,你应该迭代.bundle
中的每个文件答案 0 :(得分:8)
您可以使用GCD的调度源机制 - 使用它可以观察特定的系统事件(在您的情况下,这是vnode类型事件,因为您正在使用文件系统)。 要为特定目录设置观察者,我使用了这样的代码:
- (dispatch_source_t) fileSystemDispatchSourceAtPath:(NSString*) path
{
int fileDescr = open([path fileSystemRepresentation], O_EVTONLY);// observe file system events for particular path - you can pass here Documents directory path
//observer queue is my private dispatch_queue_t object
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileDescr, DISPATCH_VNODE_ATTRIB| DISPATCH_VNODE_WRITE|DISPATCH_VNODE_LINK|DISPATCH_VNODE_EXTEND, observerQueue);// create dispatch_source object to observe vnode events
dispatch_source_set_registration_handler(source, ^{
NSLog(@"registered for observation");
//event handler is called each time file system event of selected type (DISPATCH_VNODE_*) has occurred
dispatch_source_set_event_handler(source, ^{
dispatch_source_vnode_flags_t flags = dispatch_source_get_data(source);//obtain flags
NSLog(@"%lu",flags);
if(flags & DISPATCH_VNODE_WRITE)//flag is set to DISPATCH_VNODE_WRITE every time data is appended to file
{
NSLog(@"DISPATCH_VNODE_WRITE");
NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
float size = [[dict valueForKey:NSFileSize] floatValue];
NSLog(@"%f",size);
}
if(flags & DISPATCH_VNODE_ATTRIB)//this flag is passed when file is completely written.
{
NSLog(@"DISPATCH_VNODE_ATTRIB");
dispatch_source_cancel(source);
}
if(flags & DISPATCH_VNODE_LINK)
{
NSLog(@"DISPATCH_VNODE_LINK");
}
if(flags & DISPATCH_VNODE_EXTEND)
{
NSLog(@"DISPATCH_VNODE_EXTEND");
}
NSLog(@"file = %@",path);
NSLog(@"\n\n");
});
dispatch_source_set_cancel_handler(source, ^{
close(fileDescr);
});
});
//we have to resume dispatch_objects
dispatch_resume(source);
return source;
}
答案 1 :(得分:1)
我发现两个相当可靠(即不是100%可靠但足以满足我的需求)的方法,这些方法只与轮询目录的内容一起使用:
NSURLContentModificationDateKey
。在传输文件时,此值设置为当前日期。传输完成后,将其设置为原始文件的值:BOOL busy = (-1.0 * [modDate timeintervalSinceNow]) < pollInterval;
NSURLThumbnailDictionaryKey
。在传输文件时,此值为nil
,之后它会包含缩略图,但可能仅适用于系统可以生成缩略图的文件类型。对我来说不是问题,因为我只关心图像和视频,但也许对你而言。虽然这比解决方案1更可靠,但是如果你在导入目录中有很多文件,它会对CPU产生很大的影响,甚至可能导致你的应用程序被杀死。可以组合调度源和轮询,即当调度源检测到更改时,开始轮询直到没有剩余繁忙的文件。