iTunes文件共享应用程序:实时监控传入数据
我正在开发支持iTunes文件共享function的iOS项目。 目标是实时跟踪传入/更改的数据。
我正在使用Apple的示例代码中的(有点修改过的) DirectoryWatcher类,并尝试了这个源代码 。
数据是NSBundle(* .bundle),一些捆绑包在100-500 MB范围内,取决于其内容,一些video/音频内容。 bundle包含基于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中的每个文件
您可以使用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; }
我发现两个相当可靠(即不是100%可靠但足够可靠,满足我的需求)方法,这些方法只与轮询目录的内容一起使用:
- 检查
NSURLContentModificationDateKey
。 在传输文件时,此值将设置为当前日期。 传输完成后,将其设置为原始文件的值:BOOL busy = (-1.0 * [modDate timeintervalSinceNow]) < pollInterval;
- 检查
NSURLThumbnailDictionaryKey
。 在传输文件时,此值为nil
,之后它会包含缩略图,但可能仅适用于系统可以生成缩略图的文件类型。 对我来说不是问题,因为我只关心图像和video,但也许对你而言。 虽然这比解决方案1更可靠,但它会对CPU造成很大影响,如果导入目录中有大量文件,甚至可能导致应用程序被杀死。
可以组合调度源和轮询,即当调度源检测到更改时,开始轮询直到没有剩余繁忙文件。