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

时间:2011-05-26 10:57:17

标签: iphone ios ipad filesystems nsfilemanager

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

[fileManager copyItemAtPath:sourcePath toPath:targetPath error:&error];

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

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

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

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

9 个答案:

答案 0 :(得分:48)

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

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

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

答案 1 :(得分:22)

在这种情况下,您希望执行原子保存,最好通过NSDataNSString的{​​{1}}方法实现(和他们的变种):

writeToFile:atomically:

NSData *myData = ...; //fetched from somewhere [myData writeToFile:targetPath atomically:YES];

NSString

答案 2 :(得分: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
        }
    }
}

答案 3 :(得分:5)

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

try? FileManager.default.removeItem(at: item_destination)
try FileManager.default.copyItem(at: item, to: item_destination)

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

答案 4 :(得分:4)

为了覆盖文件,我更喜欢

NSData *imgDta = UIImageJPEGRepresentation(tImg, 1.0);

[imgDta writeToFile:targetPath options:NSDataWritingFileProtectionNone error:&err];

删除&循环复制文件有时并不像预期的那样工作

答案 5 :(得分:3)

Swift4:

_ = try FileManager.default.replaceItemAt(previousItemUrl, withItemAt: currentItemUrl)

答案 6 :(得分:1)

我认为你所用纳秒的可能性是微弱的。所以坚持第一种删除现有文件并复制新文件的方法。

答案 7 :(得分:1)

我认为您正在寻找的是NSFileManagerDelegate协议方法:

- (BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error copyingItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath;

通过此方法,您可以决定如何处理现有文件(重命名/删除),然后继续复制。

答案 8 :(得分:0)

这是为了改进问题“Move file and override [duplicate]”的“Swift 3及以上版本”,该问题与此问题重复。

将文件从sourcepath(字符串)移动到DestinationPath(字符串)。 如果DestinationPath中已存在同名文件,则删除现有文件。

// Set the correct path in string in 'let' variables.
let destinationStringPath = ""
let sourceStringPath = ""

let fileManager:FileManager = FileManager.default
do
{
    try fileManager.removeItem(atPath: sourceStringPath)
}
catch
{
}

do
{
    try fileManager.moveItem(atPath: sourceStringPath, toPath: destinationStringPath)
}
catch
{
}