File.Move ne fonctionne pas - Le fichier existe déjà

86

J'ai un dossier:

c: \ test

J'essaye ce code:

File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test");

Je reçois une exception:

Le fichier existe déjà

Le répertoire de sortie existe définitivement et le fichier d'entrée est là.

Jack Kada
la source
2
Si le fichier d'entrée est déjà dans le répertoire de sortie, alors le fichier existe déjà, expliquant ainsi l'exception. Vous devez indiquer que vous souhaitez que le fichier d'origine soit écrasé par le nouveau.
Cody Gray
9
On dirait que l'erreur vous dit exactement ce qui ne va pas.
Josh
@Josh Non. On dirait que Windows a un comportement de système de fichiers non POSIX qui rend impossible de trouver un simple modèle / routine de mise à jour de fichier transactionnel portable.
binki
@binki POSIX n'est pas pertinent (faites-vous référence aux opérations atomiques ?), NTFS prend en charge les opérations transactionnelles réelles, comme dans rollback-and-get-the-original-file-content-back. Comme d' autres ont répondu, Win32 ne permet déplacer avec remplacer. C'est File.Move .NET qui ne fournit pas la fonctionnalité. Vous pouvez obtenir à la fois des opérations de déplacement avec remplacement et des opérations transactionnelles avec des bibliothèques comme AlphaFS
Panagiotis Kanavos
2
@binki en tout cas, le comportement est bien défini sur différents systèmes de fichiers , peu importe ce que disent les discussions des forums. La raison pour laquelle File.Move n'appelle pas les méthodes Ex ou Transoted est que FAT, qui ne peut pas être ignoré car il est toujours utilisé par les cartes mémoire, n'est pas atomique et ne se comporte pas de la même manière. Les renommés ne sont pas des opérations de métadonnées et nécessitent un déplacement réel des données. Et oubliez les transactions et la copie sur écriture. Pas une bonne décision à mon humble avis
Panagiotis Kanavos

Réponses:

62

Vous devez le déplacer vers un autre fichier (plutôt qu'un dossier), cela peut également être utilisé pour le renommer.

Bouge toi:

File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test\SomeFile.txt");

Renommer:

File.Move(@"c:\test\SomeFile.txt", @"c:\test\SomeFile2.txt");

La raison pour laquelle il est indiqué «Le fichier existe déjà» dans votre exemple est que C:\test\Testtente de créer un fichier Testsans extension, mais ne peut pas le faire car un dossier existe déjà avec le même nom.

Lee
la source
138

Ce dont vous avez besoin est:

if (!File.Exists(@"c:\test\Test\SomeFile.txt")) {
    File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test\SomeFile.txt");
}

ou

if (File.Exists(@"c:\test\Test\SomeFile.txt")) {
    File.Delete(@"c:\test\Test\SomeFile.txt");
}
File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test\SomeFile.txt");

Cela va soit:

  • Si le fichier n'existe pas à l'emplacement de destination, déplacez le fichier avec succès, ou;
  • Si le fichier existe à l'emplacement de destination, supprimez-le, puis déplacez le fichier.

Edit: Je devrais clarifier ma réponse, même si c'est la plus votée! Le deuxième paramètre de File.Move doit être le fichier de destination et non un dossier. Vous spécifiez le deuxième paramètre comme dossier de destination, et non comme nom de fichier de destination - ce qui est requis par File.Move. Donc, votre deuxième paramètre devrait être c:\test\Test\SomeFile.txt.

Jamie Howarth
la source
Il n'a certainement pas besoin de vérifier si le fichier n'est pas là, car il vérifie et le fichier n'est pas là. L'exception est provoquée par le fait de ne pas ajouter le nom de fichier au dossier de destination lorsque vous essayez de le déplacer vers un autre dossier.
Hadi Eskandari
3
Si votre application est multithread (ou si d'autres processus travaillent sur vos fichiers), vous pouvez toujours obtenir la même exception, même en utilisant le code «si (existe) supprimer». Comme il y a encore un espace de temps où un autre thread / processus pourrait remettre un fichier après la suppression, alors vous effectuez votre déplacement, puis obtenez quand même l'exception.
Cela
11
Cette réponse est toujours valable pour la plupart des utilisateurs de Google après avoir essayé d'écraser un fichier existant. La plupart des gens dans cette situation difficile n'ont pas de problème de syntaxe / type-o comme l'OP.
WEFX
1
@ v.oddou fait intéressant, si le fichier n'existe pas, File.Delete fonctionne en effet correctement et ne fait rien. Si à la place, l'un des répertoires du chemin n'existe pas, vous obtenez une exception DirectoryNotFoundException.
Brandon Barkley
2
@JirkaHanika vous pouvez changer if (File.Exists) en while (File.Exists).
Brandon Barkley
38

Personnellement, je préfère cette méthode. Cela écrasera le fichier sur la destination, supprimera le fichier source et empêchera également de supprimer le fichier source lorsque la copie échoue.

string source = @"c:\test\SomeFile.txt";
string destination = @"c:\test\test\SomeFile.txt";

try
{
    File.Copy(source, destination, true);
    File.Delete(source);
}
catch
{
    //some error handling
}
Mitchell
la source
4
C'est bien pour les petits fichiers (et aucune exigence pour le déplacement atomique), mais pour les fichiers volumineux, ou les cas où vous devez être sûr de ne pas vous retrouver avec des doublons, c'est problématique.
River Satya
Pourquoi préférez-vous File.Copy , File.Deleteplus File.Move?
John Pietrar
6
File.Move n'a pas d'option d'écrasement.
Mitchell le
1
En fonction de votre cas d'utilisation, cela peut poser des problèmes. "Move" est un événement réel dans un observateur de système de fichiers. Quelque chose répertoriant les événements du système de fichiers va obtenir une suppression et un événement de création au lieu d'un événement de déplacement. Cela modifiera également l'ID du système de fichiers sous-jacent.
Andrew Rondeau
1
Cela ne sera-t-il pas beaucoup moins performant pour les gros fichiers? Si la source et la destination sont sur le même volume physique, vous créez une deuxième copie sans raison, puis supprimez l'original, alors que File.Move () évitera de faire un travail supplémentaire si la source et la destination sont sur le même volume.
Brad Westness
18

Vous pouvez faire un P / Invoke to MoveFileEx()- passer 11 pour flags( MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)

[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern bool MoveFileEx(string existingFileName, string newFileName, int flags);

Ou, vous pouvez simplement appeler

Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(existingFileName, newFileName, true);

après avoir ajouté Microsoft.VisualBasic comme référence.

mheyman
la source
Tout à fait bien si l'application ne fonctionne que sur Windows. C'est probablement une bonne réponse pour la plupart des gens qui sont prêts à essayer som P / Invoke.
Todd
9

Si le fichier existe vraiment et que vous souhaitez le remplacer, utilisez le code ci-dessous:

string file = "c:\test\SomeFile.txt"
string moveTo = "c:\test\test\SomeFile.txt"

if (File.Exists(moveTo))
{
    File.Delete(moveTo);
}

File.Move(file, moveTo);
Pawel Czapski
la source
4

Selon la documentation de File.Move, il n'y a pas de paramètre «écraser s'il existe». Vous avez essayé de spécifier le dossier de destination , mais vous devez donner la spécification complète du fichier.

En lisant à nouveau la documentation ("fournir la possibilité de spécifier un nouveau nom de fichier"), je pense que l' ajout d'une barre oblique inverse à la spécification du dossier de destination peut fonctionner.

Ekkehard.Horner
la source
Et la documentation mentionne Notez que si vous essayez de remplacer un fichier en déplaçant un fichier du même nom dans ce répertoire, une IOException est lancée. Pour cela, appelez à la Move(String, String, Boolean)place. mais cela semble être une erreur?
Kevin Scharnhorst
@KevinScharnhorst Cette réponse était 2011. La documentation inclut désormais la prise en charge de .Net Core 3.0 pour Move with Overwrite.
Todd
4

1) Avec C # sur .Net Core 3.0 et au-delà, il existe désormais un troisième paramètre booléen:

voir https://docs.microsoft.com/en-us/dotnet/api/system.io.file.move?view=netcore-3.1

In .NET Core 3.0 and later versions, you can call Move(String, String, Boolean) setting the parameter overwrite to true, which will replace the file if it exists.

2) Pour toutes les autres versions de .Net, https://stackoverflow.com/a/42224803/887092 est la meilleure réponse. Copiez avec écrasement, puis supprimez le fichier source. C'est mieux car cela en fait une opération atomique. (J'ai essayé de mettre à jour les documents MS avec ceci)

Todd
la source
2

Essayez Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(Source, Destination, True). Le dernier paramètre est le commutateur d'écrasement, qui System.IO.File.Moven'a pas.

marque
la source
2
Il y a déjà une autre réponse similaire qui suggère le même stackoverflow.com/a/42224803/1236734
JG in SD
C'est la réponse qui suggère la même chose: stackoverflow.com/a/38372760/887092 , pas stackoverflow.com/a/42224803/1236734
Todd
1

Si vous n'avez pas la possibilité de supprimer le fichier déjà existant dans le nouvel emplacement, mais que vous devez tout de même déplacer et supprimer de l'emplacement d'origine, cette astuce de changement de nom peut fonctionner:

string newFileLocation = @"c:\test\Test\SomeFile.txt";

while (File.Exists(newFileLocation)) {
    newFileLocation = newFileLocation.Split('.')[0] + "_copy." + newFileLocation.Split('.')[1];
}
File.Move(@"c:\test\SomeFile.txt", newFileLocation);

Cela suppose le seul "." dans le nom du fichier est avant l'extension. Il divise le fichier en deux avant l'extension, joint "_copy". entre. Cela vous permet de déplacer le fichier, mais crée une copie si le fichier existe déjà ou une copie de la copie existe déjà, ou une copie de la copie de la copie existe ...;)

Embardée
la source