文件提供程序扩展,importDocumentAtURL:: 无法读取给定 URL 处的文件(iOS 11.4.1)

File Provider Extension, importDocumentAtURL:: can't read file at given URL (iOS 11.4.1)

我在将操作粘贴到文件提供程序扩展中的容器时遇到问题。

如果我将复制的图像或文本粘贴到文件应用程序 -> 我的应用程序 -> 任何文件夹中,则无法读取位于 fileURL 的文件(因此无法上传到我的服务器,也无法在本地存储)。

- (void)importDocumentAtURL:(NSURL *)fileURL
     toParentItemIdentifier:(NSFileProviderItemIdentifier)parentItemIdentifier
          completionHandler:(void (^)(NSFileProviderItem _Nullable importedDocumentItem, NSError * _Nullable error))completionHandler
{

    NSError *readError = nil;
    NSData *fileData = [NSData dataWithContentsOfURL:fileURL options:NSDataReadingMappedAlways error:&readError];
    NSString *readErrorMessage = readError.localizedDescription;

    NSURL *myFileURL = [NSFileProviderManager.defaultManager.documentStorageURL URLByAppendingPathComponent:@"temp.dat"];
    NSError *copyError = nil;
    BOOL copyResult = [_fileManager copyItemAtURL:fileURL toURL:myFileURL error:&copyError];
    NSString *copyErrorMessage = copyError.localizedDescription;

    ...

readErrorMessage 和 copyErrorMessage 都是:

无法打开文件“text.txt”,因为您没有查看它的权限。

我做错了什么?

谢谢。

更新: 从我的容器、iCloud 容器复制的任何文件,以及从系统剪贴板的 text/image/other 数据生成的合成文件都会发生这种情况。

看起来您正在开发安全范围的 URL。

根据Document Picker Programming Guide

Any app that accesses documents outside its sandbox must meet the following requirements:

  • Your app must perform all file read and write operations using file coordination.

  • If you display the contents of the document to the user, you must track the document’s state using a file presenter. If you’re only showing a list of files, a file presenter is not necessary.

  • Do not save any URLs accessed through open or move operations. Always open the document using a document picker, a metadata query, or a security-scoped bookmark to the URL.

  • These operations return security-scoped URLs. You must call startAccessingSecurityScopedResource before accessing the URL.

  • If startAccessingSecurityScopedResource returns YES, call stopAccessingSecurityScopedResource when you are done using the file.

  • If you are using a UIDocument subclass, it will automatically consume the security-scoped URLs for you. There’s no need to call startAccessingSecurityScopedResource or stopAccessingSecurityScopedResource. UIDocument also acts as a file presenter and automatically handles file coordination. For these reasons, using a UIDocument subclass is highly recommended for all files outside your app’s sandbox.

因此您需要在url 处的文件被复制之前调用startAccessingSecurityScopedResource。你的代码可能会变成。

- (void)importDocumentAtURL:(NSURL *)fileURL
     toParentItemIdentifier:(NSFileProviderItemIdentifier)parentItemIdentifier
          completionHandler:(void (^)(NSFileProviderItem _Nullable importedDocumentItem, NSError * _Nullable error))completionHandler
{

  NSError *readError = nil;
  NSData *fileData = [NSData dataWithContentsOfURL:fileURL options:NSDataReadingMappedAlways error:&readError];
  NSString *readErrorMessage = readError.localizedDescription;

  NSURL *myFileURL = [NSFileProviderManager.defaultManager.documentStorageURL URLByAppendingPathComponent:@"temp.dat"];

  // Call |startAccessingSecurityScopedResource| before working on the url
  [fileURL startAccessingSecurityScopedResource];

  NSError *copyError = nil;
  BOOL copyResult = [_fileManager copyItemAtURL:fileURL toURL:myFileURL error:&copyError];
  NSString *copyErrorMessage = copyError.localizedDescription;

  // ....
  // Call |stopAccessingSecurityScopedResource| after everything is done.
  [fileURL stopAccessingSecurityScopedResource];
}