调用startAccessingSecurityScopedResource后,从Files App复制文件到沙箱失败
问题描述
我的应用支持就地打开文档,但在特定条件下需要将其复制到本地沙箱。使用NSURL startAccessingSecurityScopedResource方法后,已验证文件存在,但复制操作失败,提示文件不存在。
代码实现(App Delegate中)
if (openedInPlace) { NSString *urlString = [url absoluteString]; if ([urlString containsString:@"/Downloads/"]) { NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil]; NSError *coordinatorError = nil; __block BOOL leaveInPlace = YES; __block NSURL *fileURL; [fileCoordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingWithoutChanges error:&coordinatorError byAccessor:^(NSURL *newURL) { // Copy to local inbox NSFileManager *fm = [NSFileManager defaultManager]; NSURL *docs = [fm URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; NSURL *inbox = [docs URLByAppendingPathComponent:@"Inbox" isDirectory:YES]; //NSURL *fileURL = [inbox URLByAppendingPathComponent:[url lastPathComponent]]; fileURL = [inbox URLByAppendingPathComponent:@"test_doc.ptw"]; BOOL result; NSLog(@"Copy from %@ to %@", newURL, fileURL); result = [newURL startAccessingSecurityScopedResource]; NSLog(@"startAccessingSecurityScopedResource %@", result ? @"succeeded" : @"did not succeed."); result = [fm fileExistsAtPath:newURL.path]; NSLog(@"File %@ %@", newURL.path, result ? @"exists" : @"doesn't exist"); NSError *copyError = nil; [fm copyItemAtURL:newURL toURL:fileURL error:©Error]; if (!copyError) { // OK leaveInPlace = NO; } else { NSLog(@"Files app error: %@", copyError); leaveInPlace = YES; result = [fm fileExistsAtPath:newURL.path]; NSLog(@"File %@ %@", newURL.path, result ? @"exists" : @"doesn't exist"); } [newURL stopAccessingSecurityScopedResource]; }]; openedInPlace = leaveInPlace; if (! openedInPlace) url = fileURL; } }
日志信息
Copy from file:///private/var/mobile/Containers/Shared/AppGroup/E6E86AC8-4E2F-45B2-9842-FB6AAE29D31E/File%20Provider%20Storage/Downloads/Bloc%202.ptw to file:///var/mobile/Containers/Data/Application/912852F7-9C16-4D9C-B9C9-6A0126C92C40/Documents/Inbox/test_doc.ptw startAccessingSecurityScopedResource succeeded File /private/var/mobile/Containers/Shared/AppGroup/E6E86AC8-4E2F-45B2-9842-FB6AAE29D31E/File Provider Storage/Downloads/Bloc 2.ptw exists Files app error: Error Domain=NSCocoaErrorDomain Code=4 "The file “Bloc 2.ptw” doesn’t exist." UserInfo={NSSourceFilePathErrorKey=/private/var/mobile/Containers/Shared/AppGroup/E6E86AC8-4E2F-45B2-9842-FB6AAE29D31E/File Provider Storage/Downloads/Bloc 2.ptw, NSUserStringVariant=( Copy ), NSDestinationFilePath=/var/mobile/Containers/Data/Application/912852F7-9C16-4D9C-B9C9-6A0126C92C40/Documents/Inbox/test_doc.ptw, NSFilePath=/private/var/mobile/Containers/Shared/AppGroup/E6E86AC8-4E2F-45B2-9842-FB6AAE29D31E/File Provider Storage/Downloads/Bloc 2.ptw, NSUnderlyingError=0x28252c570 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}} File /private/var/mobile/Containers/Shared/AppGroup/E6E86AC8-4E2F-45B2-9842-FB6AAE29D31E/File Provider Storage/Downloads/Bloc 2.ptw exists
已尝试在fileCoordinator块中使用原url替代newURL,问题依旧。
解决方案
1. 确保目标目录存在
当前代码仅通过URLByAppendingPathComponent拼接目录路径,但未确保目录实际创建。添加目录创建逻辑:
NSFileManager *fm = [NSFileManager defaultManager]; NSURL *docs = [fm URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; NSURL *inbox = [docs URLByAppendingPathComponent:@"Inbox" isDirectory:YES]; // 确保Inbox目录存在,不存在则创建 NSError *dirError = nil; if (![fm createDirectoryAtURL:inbox withIntermediateDirectories:YES attributes:nil error:&dirError]) { NSLog(@"Failed to create Inbox directory: %@", dirError); return; }
2. 调整安全范围资源的调用时机
对于File Provider提供的文件,startAccessingSecurityScopedResource应在进入File Coordinator访问器之前调用,避免权限失效:
if (openedInPlace) { NSString *urlString = [url absoluteString]; if ([urlString containsString:@"/Downloads/"]) { // 先获取安全范围资源访问权限 BOOL hasAccess = [url startAccessingSecurityScopedResource]; if (!hasAccess) { NSLog(@"Failed to access security scoped resource"); return; } NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil]; NSError *coordinatorError = nil; __block BOOL leaveInPlace = YES; __block NSURL *fileURL; [fileCoordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingWithoutChanges error:&coordinatorError byAccessor:^(NSURL *newURL) { NSFileManager *fm = [NSFileManager defaultManager]; NSURL *docs = [fm URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; NSURL *inbox = [docs URLByAppendingPathComponent:@"Inbox" isDirectory:YES]; // 创建Inbox目录 NSError *dirError = nil; if (![fm createDirectoryAtURL:inbox withIntermediateDirectories:YES attributes:nil error:&dirError]) { NSLog(@"Create Inbox error: %@", dirError); leaveInPlace = YES; return; } fileURL = [inbox URLByAppendingPathComponent:@"test_doc.ptw"]; // 若目标文件已存在,先删除避免复制失败 if ([fm fileExistsAtPath:fileURL.path]) { [fm removeItemAtURL:fileURL error:nil]; } NSError *copyError = nil; BOOL copySuccess = [fm copyItemAtURL:newURL toURL:fileURL error:©Error]; if (copySuccess) { leaveInPlace = NO; } else { NSLog(@"Copy error: %@", copyError); leaveInPlace = YES; } }]; // 释放安全范围资源访问权限 [url stopAccessingSecurityScopedResource]; openedInPlace = leaveInPlace; if (!openedInPlace) { url = fileURL; } } }
3. 处理符号链接问题
File Provider返回的URL可能是符号链接,直接使用path可能无法访问真实文件。可通过NSFileManager获取真实路径:
NSString *realPath = [fm destinationOfSymbolicLinkAtPath:newURL.path error:nil]; NSURL *realURL = [NSURL fileURLWithPath:realPath]; // 使用realURL执行复制操作
4. 验证应用权限配置
确保应用已正确配置:
- 开启iCloud能力(若涉及iCloud Drive的Downloads目录),并在
Info.plist中添加NSUbiquitousContainers配置 - 确认
UIFileSharingEnabled和LSSupportsOpeningDocumentsInPlace已设置为YES
内容的提问来源于stack exchange,提问作者perdrix
相关产品推荐
相关产品推荐

