复制时如何用NSFileManager覆盖文件?

Pro*_*ber 47 filesystems iphone nsfilemanager ipad ios

我正在使用此方法复制文件:

[fileManager copyItemAtPath:sourcePath toPath:targetPath error:&error];
Run Code Online (Sandbox Code Playgroud)

我想在文件存在时覆盖它.此方法的默认行为是抛出异常/错误"文件存在".当文件存在时.没有选项可以指定它应该覆盖.

那么最安全的方法是什么?

我会首先检查文件是否存在,然后将其删除,然后尝试复制?这有一个危险,即应用程序或设备在删除文件后的纳秒内关闭,但新文件尚未复制到该位置.那没什么.

也许我必须首先更改新文件的名称,然后删除旧的,然后重新更改新的名称?同样的问题.如果在这纳秒内应用程序或设备关闭并且重命名不会发生怎么办?

mz2*_*mz2 49

如果您不想/不想将文件内容保留在内存中但希望按照其他建议中的说明进行原子重写,则可以先将原始文件复制到临时目录中,然后再将其复制到唯一路径(Apple的文档建议使用临时目录),然后使用NSFileManager

-replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:

根据参考文档,此方法"以确保不会发生数据丢失的方式替换指定URL处的项目内容." (参考文献).需要将原始文件复制到临时目录,因为此方法会移动原始文件. 这是关于的NSFileManager参考文档-replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:


Jac*_*kin 22

你想要做一个原子保存在这种情况下,这将是最好的使用达到NSDataNSStringwriteToFile:atomically:方法(和它们的变体):

NSData *myData = ...; //fetched from somewhere
[myData writeToFile:targetPath atomically:YES];
Run Code Online (Sandbox Code Playgroud)

或者NSString:

NSString *myString = ...;
NSError *err = nil;
[myString writeToFile:targetPath atomically:YES encoding:NSUTF8StringEncoding error:&err];
if(err != nil) {
  //we have an error.
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您不想将整个文件加载到RAM中(因为它很大)会怎样?加载文件似乎可能有点低效. (18认同)
  • @JacobRelkin此方法不会覆盖现有文件,因此无法使用它. (7认同)

小智 8

斯威夫特4:

_ = try FileManager.default.replaceItemAt(previousItemUrl, withItemAt: currentItemUrl)
Run Code Online (Sandbox Code Playgroud)


Tyl*_*ong 6

检测文件存在错误,删除目标文件并再次复制.

Swift 2.0中的示例代码:

class MainWindowController: NSFileManagerDelegate {

    let fileManager = NSFileManager()

    override func windowDidLoad() {
        super.windowDidLoad()
        fileManager.delegate = self
        do {
            try fileManager.copyItemAtPath(srcPath, toPath: dstPath)
        } catch {
            print("File already exists at \'\(srcPath)\':\n\((error as NSError).description)")
        }
    }

    func fileManager(fileManager: NSFileManager, shouldProceedAfterError error: NSError, copyingItemAtPath srcPath: String, toPath dstPath: String) -> Bool {
        if error.code == NSFileWriteFileExistsError {
            do {
                try fileManager.removeItemAtPath(dstPath)
                print("Existing file deleted.")
            } catch {
                print("Failed to delete existing file:\n\((error as NSError).description)")
            }
            do {
                try fileManager.copyItemAtPath(srcPath, toPath: dstPath)
                print("File saved.")
            } catch {
                print("File not saved:\n\((error as NSError).description)")
            }
            return true
        } else {
            return false
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Ajm*_*mal 5

如果您不确定文件是否存在,则可以在3+版本上使用

try? FileManager.default.removeItem(at: item_destination)
try FileManager.default.copyItem(at: item, to: item_destination)
Run Code Online (Sandbox Code Playgroud)

第一行失败,如果该文件不存在,则将其忽略。如果第二行出现异常,它将按预期方式引发。