iPhone-如何在复制时使用NSFileManager覆盖文件?

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

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

我想覆盖一个已经存在的文件。 此方法的默认行为是引发异常/错误“文件已存在”。 文件存在时。 没有选项来指定它应该覆盖。

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

我先检查文件是否存在,然后删除它,然后再尝试复制吗? 这样做的危险是,删除文件后,应用程序或设备会在毫微秒内关闭,但新文件尚未复制到该位置。 那什么都没有

也许我必须先更改新文件的名称,然后删除旧文件,然后重新更改新文件的名称? 同样的问题。 如果在十亿分之一秒内应用程序或设备关闭并且没有重命名怎么办?

9个解决方案
49 votes

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

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

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

mz2 answered 2020-06-29T23:59:08Z
22 votes

在这种情况下,您需要进行原子保存,最好使用NSStringNSStringwriteToFile:atomically:方法(及其变体)来实现:

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

NSString

NSString *myString = ...;
NSError *err = nil;
[myString writeToFile:targetPath atomically:YES encoding:NSUTF8StringEncoding error:&err];
if(err != nil) {
  //we have an error.
}
Jacob Relkin answered 2020-06-29T23:58:43Z
6 votes

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

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
        }
    }
}
Tyler Long answered 2020-06-29T23:59:32Z
5 votes

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

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

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

Ajmal Kunnummal answered 2020-06-29T23:59:57Z
4 votes

对于覆盖文件,我更喜欢

NSData *imgDta = UIImageJPEGRepresentation(tImg, 1.0);

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

循环中删除和复制文件有时无法正常工作

nirvana74v answered 2020-06-30T00:00:21Z
3 votes

Swift4:

_ = try FileManager.default.replaceItemAt(previousItemUrl, withItemAt: currentItemUrl)
Sajede Nouri answered 2020-06-30T00:00:41Z
1 votes

我认为您提到的纳秒级可能性很小。 因此,请坚持删除现有文件并复制新文件的第一种方法。

ArunGJ answered 2020-06-30T00:01:01Z
1 votes

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

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

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

ataranlen answered 2020-06-30T00:01:25Z
0 votes

这是为了改进问题“移动文件并覆盖[重复]”的“快速3及更高版本”,该问题被标记为该问题的重复项。

将文件从sourcepath(string)移到DestinationPath(string)。如果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
{
}
SHS answered 2020-06-30T00:01:50Z
translate from https://stackoverflow.com:/questions/6137423/how-to-overwrite-a-file-with-nsfilemanager-when-copying