You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用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:&copyError];
            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:&copyError];
            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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 12:02:10