web-dev-qa-db-ja.com

コピー時にNSFileManagerでファイルを上書きする方法は?

私はこの方法を使用してファイルをコピーしています:

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

既に存在するファイルを上書きしたい。このメソッドのデフォルトの動作では、例外/エラー「File Exists」がスローされます。ファイルが存在するとき。上書きすることを指定するオプションはありません。

これを行う最も安全な方法は何でしょうか?

最初にファイルが存在するかどうかを確認し、次に削除してからコピーを試みますか?これには、ファイルが削除された後、新しいファイルがその場所にコピーされていないナノ秒後にアプリまたはデバイスがオフになる危険があります。その後、何もありません。

最初に新しいファイルの名前を変更し、次に古いファイルを削除してから、新しいファイルの名前を再変更する必要があるかもしれません。同じ問題。このナノ秒でアプリまたはデバイスがオフになり、名前の変更が行われない場合はどうなりますか?

46
Proud Member

この場合、atomic saveを実行する必要があります。これは、NSDataまたはNSStringwriteToFile: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.
}
22
Jacob Relkin

ファイルの内容をメモリに保存したくない/したくないが、他の提案に記載されているようにアトミックな書き換えが必要な場合は、最初に元のファイルを一時ディレクトリに一意のパスにコピーすることができます(Appleのドキュメントでは、一時ディレクトリ)、NSFileManagerの

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

参照ドキュメントによると、このメソッドは「データの損失が発生しないように、指定されたURLのアイテムのコンテンツを置き換えます」。 (参照ドキュメントから)。このメソッドは元のファイルを移動するため、一時ディレクトリへの元のコピーが必要です。 これは-replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:に関するNSFileManagerリファレンスドキュメントです

49
mz2

ファイルの存在エラーを検出し、宛先ファイルを削除して、再度コピーします。

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
        }
    }
}
6
Tyler Long

ファイルが存在するかどうかわからない場合、Swift 3+

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

最初の行は失敗し、ファイルが存在しない場合は無視されます。 2行目で例外が発生した場合は、必要に応じてスローします。

5
Ajmal Kunnummal

ファイルを上書きするには、

NSData *imgDta = UIImageJPEGRepresentation(tImg, 1.0);

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

ループ内のファイルの削除とコピーが意図したとおりに機能しないことがある

4
nirvana74v

Swift4:

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

あなたがナノ秒を調整した可能性は弱いと思います。したがって、既存のファイルを削除して新しいファイルをコピーする最初の方法に固執します。

1
ArunGJ

あなたが探しているのはNSFileManagerDelegateプロトコルメソッドだと思います:

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

この方法から、既存のファイルの処理(名前の変更/削除)を決定し、コピーを続行できます。

1
ataranlen

これは、質問 ' ファイルを移動して[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
{
}
0
SHS