从磁盘读取文件时捕获错误 - 您没有权限



我在AppDel中使用以下代码。当用户点击 gpx 文件或使用共享选项与我的应用程序共享文件时,会触发此操作。此时,是用户指定他们允许我的应用程序访问该文件,所以我有点困惑为什么仍然被拒绝。任何建议非常感谢。

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {

if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent(url.lastPathComponent)
if FileManager.default.fileExists(atPath: fileURL.path) {
print("File already exists")
}
else {
do {
try FileManager.default.copyItem(at: url, to: fileURL)
print("Did write file to disk")
}
catch {
DispatchQueue.main.async(){
print("Catch error writing file to disk: (error)")
}
}
}
}
return true
}

错误将打印到控制台,如下所示:

错误域=

NSCocoa错误域代码=257"文件 无法打开"Beech_Hill_Long_Route.gpx",因为您没有 允许查看它。 UserInfo={NSFilePath=/private/var/mobile/Library/Mobile 文档/com~苹果~CloudDocs/Desktop/Beech_Hill_Long_Route.gpx, NSUnderlyingError=0x282c19a70 {Error Domain=NSPOSIXErrorDomain Code=1 "不允许操作"}}

我对它在iOS上的工作原理不太了解,但我确实对它如何在macOS上工作有一些了解,两者应该是相似的。 对于 macOS 沙盒应用程序,用户必须专门通过NSOpenPanelNSSavePanel选择沙盒外部的文件。 iOS上的等效物将是UIDocumentPickerViewController,但我认为显式共享也可以。基于此,如果我遇到您的问题,我会如何思考以及我会尝试什么:

你有两个URL参与你的FileManager.default.copy()电话。 目前尚不清楚哪一个产生了错误,所以我会同时考虑两者。

首先让我们看看fileURL. 您正在构造它以将文件放入用户的Documents目录中。至少在macOS上,该目录不在应用程序的沙箱中,这意味着通过NSSavePanel询问用户(这也意味着他们可能会决定将其放在其他地方)。 您可能需要执行等效UIKit操作,或者只是确保选择沙盒中的位置。

要测试这一点,请尝试写入fileURL以隔离该副本,而不是执行复制。例如:

do { try "TestString".data(using: .utf8)?.write(url: fileURL) }
catch { print("Write failed: (error.localizedDescription)") }

如果失败,那么您的问题出在fileURL,在这种情况下,您可能需要使用UIDocumentPickerViewController来保存它,或者选择一个肯定在应用程序沙盒中的位置。

如果测试成功,则问题一定出在传入的URL

。我将假设url已经是安全范围的,因为我不确定共享在其他情况下如何工作。 我认为最有可能发生在幕后的情况是,当用户与您的应用程序共享URL时,iOS 会从URL创建一个安全范围的书签,并将书签而不是URL发送到您的应用程序。 然后在应用端,该书签用于重新构建URL,然后再将其传递给应用的委托。 如果我是对的,则需要打开和关闭安全作用域才能使用它:

url.startAccessingSecurityScopedResource()
// access your file here
url.stopAccessingSecurityScopedResource()

请注意,stopAccessingSecurityScopeResource()必须在主线程上调用,因此,如果您的代码是异步发生的,则需要安排它在那里运行:

url.startAccessingSecurityScopedResource()
// access your file here
DispatchQueue.main.async { url.stopAccessingSecurityScopedResource() }

如果您需要保存URL本身以在将来运行程序时使用...你不能。 好吧,你可以,但它无效,所以你会马上回到权限错误。 相反,您必须保存一个书签,然后在将来的运行中,从书签重建URL

let bookmark = try url.bookmarkData(
options: .withSecurityScope, 
includingResourceValuesForKeys: nil, 
relativeTo: nil
)

bookmarkData的一个实例,所以你可以把它写到UserDefaults或任何你想保存的地方。 要稍后从中获取URL

var isStale = false
let url = try URL(
resolvingBookmarkData: bookmark, 
options: .withSecurityScope, 
relativeTo: nil, 
bookmarkDataIsStale: &isStale
)
if isStale 
{
let newBookmark = try url.bookmarkData(
options: .withSecurityScope, 
includingResourceValuesForKeys: nil, 
relativeTo: nil
)
// Save the new bookmark
}

请注意,如果在初始值设定项返回后trueisStale,则需要重新制作并重新保存书签。

很遗憾,我们必须经历这么多麻烦,但我们生活在一个有些人坚持对其他人的设备和数据做坏事的世界,所以这就是我们必须处理的问题保护用户免受恶意数据泄露。

最新更新