如何在不先将文件下载到设备的情况下从 iCloud 删除文件?
How can I delete a file from iCloud without downloading it to the device first?
我有一个显示文档列表的基本 iOS 应用程序。我正在尝试删除文档,但注意到如果文档尚未从 iCloud 下载到设备.[=12=,下面的代码会失败 "No such file or directory" ]
文档可能非常大 (40MB),我想避免下载文档只是为了删除它(这会占用用户数据计划中的时间和带宽)。这可能吗?
[[[NSFileCoordinator alloc] initWithFilePresenter:nil]
coordinateWritingItemAtURL:documentURL
options:NSFileCoordinatorWritingForDeleting
writingItemAtURL:previewURL
options:NSFileCoordinatorWritingForDeleting
error:&error
byAccessor:^(NSURL *newDocumentURL, NSURL *newPreviewURL){
// Fails with "No such file" error if not yet downloaded from iCloud:
[[NSFileManager defaultManager] removeItemAtURL:newDocumentURL error:&error];
[[NSFileManager defaultManager] removeItemAtURL:newPreviewURL error:&error];
}];
完整错误:
Error Domain=NSCocoaErrorDomain Code=4 "The operation couldn’t be completed.
(Cocoa error 4.)" UserInfo=0x14e82930 {NSUnderlyingError=0x14e69220
"The operation couldn’t be completed. No such file or directory",
如果您只需要删除一个文件,请使用另一个 NSFileCoordinator coordinateWritingItemAtURL(访问块中带有单个 newURL 参数的那个)。
如果需要批量删除,那么创建一个NSFileAccessIntent数组,使用NSFileCoordinator的coordinateAccessWithIntents。
示例:
- ( void )deleteItemsAtURLs: ( NSArray * )urls queue: ( NSOperationQueue * )queue
{
//assuming urls is an array of urls to be deleted
NSFileCoordinator * coordinator;
NSMutableArray * writingIntents;
NSURL * url;
writingIntents = [ NSMutableArray arrayWithCapacity: urls.count ];
for( url in urls )
{
[ writingIntents addObject: [ NSFileAccessIntent writingIntentWithURL: url options: NSFileCoordinatorWritingForDeleting ] ];
}
coordinator = [ [ NSFileCoordinator alloc ] initWithFilePresenter: nil ];
[ coordinator coordinateAccessWithIntents: writingIntents
queue: queue
byAccessor: ^( NSError * error )
{
if( error )
{
//handle
return;
}
NSFileAccessIntent * intent;
error = nil;
for( intent in writingIntents )
{
[ [ NSFileManager defaultManager ] removeItemAtURL: intent.URL error: &error ];
if( error )
{
//handle
}
}
}];
}
注意:Apple 曾经有一些示例代码来说明这一点,但可惜它已被删除。
正如答案中所指出的,您需要在循环中调用此方法,因为您需要为每个要删除的文件单独 NSFileCoordinator
。
我错过的一点是,您必须使用您创建的 NSFileAccessIntent
对象的 URL 来调用 fileManager.removeItemAtURL
来删除,而 而不是 您可以从 NSMetadataQueryItem
.
访问的普通 URL
func removeFile(at url: URL, completionHandler: ((Error?) -> Void)? = nil) {
// `url` may be a security scoped resource.
let successfulSecurityScopedResourceAccess = url.startAccessingSecurityScopedResource()
let fileCoordinator = NSFileCoordinator()
let writingIntent = NSFileAccessIntent.writingIntent(with: url, options: .forDeleting)
fileCoordinator.coordinate(with: [writingIntent], queue: backgroundQueue) { (accessError) in
if accessError != nil {
completionHandler?(accessError)
return
}
let fileManager = FileManager()
var error: Error?
do {
try fileManager.removeItem(at: writingIntent.url)
}
catch let fileError {
error = fileError
}
if successfulSecurityScopedResourceAccess {
url.stopAccessingSecurityScopedResource()
}
completionHandler?(error)
}
}
如果您想删除多个项目:
for url in urlsToDelete {
removeFile(at: url)
}
我有一个显示文档列表的基本 iOS 应用程序。我正在尝试删除文档,但注意到如果文档尚未从 iCloud 下载到设备.[=12=,下面的代码会失败 "No such file or directory" ]
文档可能非常大 (40MB),我想避免下载文档只是为了删除它(这会占用用户数据计划中的时间和带宽)。这可能吗? 完整错误:[[[NSFileCoordinator alloc] initWithFilePresenter:nil]
coordinateWritingItemAtURL:documentURL
options:NSFileCoordinatorWritingForDeleting
writingItemAtURL:previewURL
options:NSFileCoordinatorWritingForDeleting
error:&error
byAccessor:^(NSURL *newDocumentURL, NSURL *newPreviewURL){
// Fails with "No such file" error if not yet downloaded from iCloud:
[[NSFileManager defaultManager] removeItemAtURL:newDocumentURL error:&error];
[[NSFileManager defaultManager] removeItemAtURL:newPreviewURL error:&error];
}];
Error Domain=NSCocoaErrorDomain Code=4 "The operation couldn’t be completed.
(Cocoa error 4.)" UserInfo=0x14e82930 {NSUnderlyingError=0x14e69220
"The operation couldn’t be completed. No such file or directory",
如果您只需要删除一个文件,请使用另一个 NSFileCoordinator coordinateWritingItemAtURL(访问块中带有单个 newURL 参数的那个)。
如果需要批量删除,那么创建一个NSFileAccessIntent数组,使用NSFileCoordinator的coordinateAccessWithIntents。
示例:
- ( void )deleteItemsAtURLs: ( NSArray * )urls queue: ( NSOperationQueue * )queue
{
//assuming urls is an array of urls to be deleted
NSFileCoordinator * coordinator;
NSMutableArray * writingIntents;
NSURL * url;
writingIntents = [ NSMutableArray arrayWithCapacity: urls.count ];
for( url in urls )
{
[ writingIntents addObject: [ NSFileAccessIntent writingIntentWithURL: url options: NSFileCoordinatorWritingForDeleting ] ];
}
coordinator = [ [ NSFileCoordinator alloc ] initWithFilePresenter: nil ];
[ coordinator coordinateAccessWithIntents: writingIntents
queue: queue
byAccessor: ^( NSError * error )
{
if( error )
{
//handle
return;
}
NSFileAccessIntent * intent;
error = nil;
for( intent in writingIntents )
{
[ [ NSFileManager defaultManager ] removeItemAtURL: intent.URL error: &error ];
if( error )
{
//handle
}
}
}];
}
注意:Apple 曾经有一些示例代码来说明这一点,但可惜它已被删除。
正如答案中所指出的,您需要在循环中调用此方法,因为您需要为每个要删除的文件单独 NSFileCoordinator
。
我错过的一点是,您必须使用您创建的 NSFileAccessIntent
对象的 URL 来调用 fileManager.removeItemAtURL
来删除,而 而不是 您可以从 NSMetadataQueryItem
.
URL
func removeFile(at url: URL, completionHandler: ((Error?) -> Void)? = nil) {
// `url` may be a security scoped resource.
let successfulSecurityScopedResourceAccess = url.startAccessingSecurityScopedResource()
let fileCoordinator = NSFileCoordinator()
let writingIntent = NSFileAccessIntent.writingIntent(with: url, options: .forDeleting)
fileCoordinator.coordinate(with: [writingIntent], queue: backgroundQueue) { (accessError) in
if accessError != nil {
completionHandler?(accessError)
return
}
let fileManager = FileManager()
var error: Error?
do {
try fileManager.removeItem(at: writingIntent.url)
}
catch let fileError {
error = fileError
}
if successfulSecurityScopedResourceAccess {
url.stopAccessingSecurityScopedResource()
}
completionHandler?(error)
}
}
如果您想删除多个项目:
for url in urlsToDelete {
removeFile(at: url)
}