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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6137423/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 20:05:50  来源:igfitidea点击:

How to overwrite a file with NSFileManager when copying?

iphoneiosipadfilesystemsnsfilemanager

提问by Proud Member

I'm using this method to copy a file:

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

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

I want to overwrite a file when it exists already. The default behavior of this method is to throw an exception/error "File Exists." when the file exists. There's no option to specify that it should overwrite.

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

So what would be the safest way to do this?

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

Would I first check if the file exists, then delete it, and then attempt to copy? This has the danger that the app or device goes OFF right in the nanosecond after the file has been deleted but the new file hasn't been copied to that place. Then there's nothing.

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

Maybe I would have to change the name of the new file first, then delete the old, and then re-change the name of the new? Same problem. What if in this nanosecond the app or device goes OFF and renaming doesn't happen?

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

采纳答案by Jacob Relkin

You'd want to do an atomic savein this case, which would be best achieved by using NSDataor NSString's writeToFile:atomically:methods (and their variants):

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

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

Or for an NSString:

或者对于一个NSString

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

回答by mz2

If you can't/don't want to keep the file contents in memory but want an atomic rewrite as noted in the other suggestions, you can first copy the original file to a temp directory to a unique path (Apple's documentation suggests using a temporary directory), then use NSFileManager's

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

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

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

According to the reference documentation, this method 'replaces the contents of the item at the specified URL in a manner that insures no data loss occurs.' (from reference documentation). The copying of the original to the temporary directory is needed because this method moves the original file. Here's the NSFileManager reference documentation about -replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:

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

回答by Ajmal Kunnummal

If you're not sure if the file exists, this works on swift 3+

如果你不知道文件是否存在,在这个迅速作品3+

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

The first line fails and is ignored if the file doesn't already exist. If there's a exception durning the second line, it throws as it should.

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

回答by Tyler Long

Detect file exists error, delete the destination file and copy again.

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

Sample code in Swift 2.0:

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

回答by Sajede Nouri

Swift4:

斯威夫特4:

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

回答by nirvana74v

For overwriting files, I prefer

对于覆盖文件,我更喜欢

NSData *imgDta = UIImageJPEGRepresentation(tImg, 1.0);

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

Removing & copying files in loop sometimes doesn't work as intended

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

回答by ataranlen

I think what you're looking for is the NSFileManagerDelegate protocol method:

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

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

From this method, you can decide what to do with the existing file (rename/delete) and then proceed with the copy.

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

回答by ArunGJ

I think the possibility of the nanosecond you mensioned is feeble. so stick to the first method of removing the existing file and copying the new file.

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

回答by SHS

This is for improvement of 'Swift 3 and above' of question 'Move file and override [duplicate]' which is marked duplicate of this question.

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

To move file from sourcepath(string) to DestinationPath(string). Delete the existing file if same name file already exists at DestinationPath.

将文件从 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
{
}