web-dev-qa-db-fra.com

Comment écraser un fichier avec NSFileManager lors de la copie?

J'utilise cette méthode pour copier un fichier:

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

Je veux écraser un fichier lorsqu'il existe déjà. Le comportement par défaut de cette méthode consiste à lever une exception/erreur "Le fichier existe". lorsque le fichier existe. Il n'y a pas d'option pour spécifier qu'il doit remplacer.

Quelle serait donc la façon la plus sûre de procéder?

Dois-je d'abord vérifier si le fichier existe, puis le supprimer, puis tenter de le copier? Cela présente le danger que l'application ou l'appareil s'éteigne juste dans la nanoseconde après la suppression du fichier, mais le nouveau fichier n'a pas été copié à cet endroit. Ensuite, il n'y a rien.

Peut-être devrais-je d'abord changer le nom du nouveau fichier, puis supprimer l'ancien, puis changer à nouveau le nom du nouveau? Même problème. Que se passe-t-il si dans cette nanoseconde l'application ou l'appareil s'éteint et que le changement de nom ne se produit pas?

46
Proud Member

Vous voudriez faire un sauvegarde atomique dans ce cas, ce qui serait mieux réalisé en utilisant NSData ou NSString's writeToFile:atomically: méthodes (et leurs variantes):

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

Ou pour un 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

Si vous ne pouvez pas/ne souhaitez pas conserver le contenu du fichier en mémoire mais souhaitez une réécriture atomique comme indiqué dans les autres suggestions, vous pouvez d'abord copier le fichier d'origine dans un répertoire temporaire vers un chemin unique (la documentation d'Apple suggère d'utiliser un répertoire temporaire), puis utilisez NSFileManager

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

Selon la documentation de référence, cette méthode "remplace le contenu de l'élément à l'URL spécifiée de manière à éviter toute perte de données". (à partir de la documentation de référence). La copie de l'original dans le répertoire temporaire est nécessaire car cette méthode déplace le fichier d'origine. Voici la documentation de référence NSFileManager sur -replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:

49
mz2

Erreur de détection de fichier existe, supprimez le fichier de destination et copiez à nouveau.

Exemple de code dans 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

Si vous n'êtes pas sûr que le fichier existe, cela fonctionne sur Swift 3+

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

La première ligne échoue et est ignorée si le fichier n'existe pas déjà. S'il y a une exception pendant la deuxième ligne, elle se déclenche comme il se doit.

5
Ajmal Kunnummal

Pour écraser les fichiers, je préfère

NSData *imgDta = UIImageJPEGRepresentation(tImg, 1.0);

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

La suppression et la copie de fichiers en boucle ne fonctionnent parfois pas comme prévu

4
nirvana74v

Swift4:

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

Je pense que la possibilité de la nanoseconde que vous avez mesurée est faible. restez donc à la première méthode pour supprimer le fichier existant et copier le nouveau fichier.

1
ArunGJ

Je pense que ce que vous recherchez est la méthode du protocole NSFileManagerDelegate:

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

À partir de cette méthode, vous pouvez décider quoi faire avec le fichier existant (renommer/supprimer), puis procéder à la copie.

1
ataranlen

C'est pour l'amélioration de 'Swift 3 et supérieur' de la question ' Déplacer le fichier et remplacer [duplicate] ' qui est marqué comme doublon de cette question.

Pour déplacer le fichier du chemin source (chaîne) vers DestinationPath (chaîne). Supprimez le fichier existant si le fichier de même nom existe déjà sur 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