繁体   English   中英

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

[英]How to overwrite a file with NSFileManager when copying?

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

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

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

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

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

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

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

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

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

在这种情况下,您需要进行原子保存,最好使用NSDataNSStringwriteToFile: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.
}

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

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
        }
    }
}

如果您不确定该文件是否存在,这适用于 swift 3+

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

如果文件不存在,第一行将失败并被忽略。 如果在第二行出现异常,它会按应有的方式抛出。

对于覆盖文件,我更喜欢

NSData *imgDta = UIImageJPEGRepresentation(tImg, 1.0);

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

在循环中删除和复制文件有时无法按预期工作

斯威夫特4:

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

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

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

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

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

这是为了改进问题“ 移动文件并覆盖 [duplicate] ”的“Swift 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
{
}

以下代码可确保复制成功而不会损坏或丢失文件,即使应用程序随时终止也是如此

if try FileManager.default.fileExists(atPath: target.path) == true {
    try fileManager.replaceItemAt(target, withItemAt: source)
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM