99

I've got a folder:

c:\test

I'm trying this code:

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

I get exception:

File already exists

The output directory definitely exists and the input file is there.

Ray Hayes
  • 14,896
  • 8
  • 53
  • 78
Jack Kada
  • 24,474
  • 29
  • 82
  • 106
  • 2
    If the input file is already in the output directory, then the file already exists, thus explaining the exception. You need to indicate that you want the original file *overwritten* by the new one. – Cody Gray - on strike May 07 '11 at 12:06
  • 11
    Sounds like the error is telling you exactly what's wrong. – Josh May 07 '11 at 12:07
  • @Josh No. It sounds like Windows is having non-POSIX filesystem behavior which make figuring out a simple portable transactional file update pattern/routine impossible. – binki Jan 24 '16 at 06:53
  • @binki POSIX is irrelevant (are you refering to *atomic* operations?), NTFS *does* support real transactional operations, as in rollback-and-get-the-original-file-content-back. As others answered, Win32 *does* allow move with replace. I'ts .NET's File.Move that doesn't provide the functionality. You can get both Move with replace and transactional operations with libraries like AlphaFS – Panagiotis Kanavos Feb 20 '18 at 10:19
  • @PanagiotisKanavos .net’s APIs are a reflection of the win32 API but excludes the useful things like [`FileMoveEx()`](https://stackoverflow.com/questions/5920882/file-move-does-not-work-file-already-exists?noredirect=1#38372760). As a result, .net lacks a POSIX-like FS layer. .net does expose `File.Replace()`, but trying to implement a POSIX-like `rename()` in terms of it results in a race condition. If the framework exposed a POSIX-like `rename()`, it would make peoples’ lives easier and allow people to write more pure (less P/Invoke) code. – binki Feb 20 '18 at 16:30
  • Except [no-one seems to say](https://stackoverflow.com/q/167414/429091) [whether or not `FileMoveEx()` with `MOVEFILE_REPLACE_EXISTING` is atomic](https://lwn.net/Articles/682988/) (though [a forums person suggests it is likely atomic on same volume source/destination for NTFS](https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/449bb49d-8acc-48dc-a46f-0760ceddbfc3/movefileexmovefilereplaceexisting-ntfs-same-volume-atomic?forum=windowssdk)). – binki Feb 20 '18 at 16:53
  • @binki would it make sense to say that Java, Python or R don't have a POSIX-like FS layer? That's an OS feature, not a language or runtime feature. And transactions, in the full distributed sense *are* available through TxF. You can't use POSIX to enlist file and database operations under the same distributed transaction. POSIX has nothing to do with [which Win32 API function](https://referencesource.microsoft.com/#mscorlib/microsoft/win32/win32native.cs,1444) is called by File.Move. – Panagiotis Kanavos Feb 21 '18 at 08:36
  • 3
    @binki in any case the behaviour is well defined *on different file systems*, no matter what forums discussions say. The reason File.Move doesn't call the Ex or Transacted methods is that FAT, which can't be ignored since it's still used by memory cards, *isn't* atomic and *doesn't* behave the same. Renames aren't metadata operations and require actual data movement. And forget about transactions & copy-on-write. Not a great decision imho – Panagiotis Kanavos Feb 21 '18 at 08:42

9 Answers9

164

What you need is:

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

or

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");

This will either:

  • If the file doesn't exist at the destination location, successfully move the file, or;
  • If the file does exist at the destination location, delete it, then move the file.

Edit: I should clarify my answer, even though it's the most upvoted! The second parameter of File.Move should be the destination file - not a folder. You are specifying the second parameter as the destination folder, not the destination filename - which is what File.Move requires. So, your second parameter should be c:\test\Test\SomeFile.txt.

anon
  • 4,578
  • 3
  • 35
  • 54
Jamie Howarth
  • 3,273
  • 3
  • 20
  • 26
  • Certainly does not need to check if the file is not there, because he's checking and the file is not there. The exception is caused by not appending the file name to destination folder when trying to move it to another folder. – Hadi Eskandari Mar 16 '12 at 15:16
  • Thanks a lot. I was putting just the folder name (which i thought was the logical thing to do). But come to think of it, if destination was just a folder name, then i will not be able to move and rename the file in one go. – Siddharth Aug 15 '12 at 14:07
  • 4
    If your app is multi threaded (or there are other processes working on your files) you could possibly still get the same exception even using the "if(Exists) Delete" code. As there is still a space of time where another thread/process could be putting a file back after the Delete, then you do your move, and then get the Exception anyway. Worth just bearing in mind :-) – bytedev Jan 17 '13 at 15:43
  • 14
    This answer is still valid for most people that google after trying to overwrite an existing file. Most people in this predicament don't have a syntax/type-o issue like the OP. – WEFX Apr 11 '13 at 13:28
  • as a supporter of **if**-less programming style, I would say it would be better style to not have the 'if exists' logic written as such high level. the Delete should simply silently do nothing if not exist. – v.oddou Nov 14 '14 at 06:38
  • @WEFX - I agree this answer is helpful for a lot of users. However the OP didn't have a syntax or typeo issue, they simply believed that the File.Move operation moves files to another folder, but it does not, it moves files to another file - which is counter-intuitive (however more powerful). – Lee Jun 29 '15 at 14:35
  • @nashwan I'm considering improving this answer with a file-locking example to ensure that there's no potential race condition between the File.Exists & File.Delete calls, although I think it's outside the scope of the question. – Jamie Howarth Mar 12 '16 at 04:30
  • This does not work on Windows reliably, even if single threaded. It may take a while after the File.Delete returns, before the NTFS directory entry for the file evolves from "I'm being deleted" to "I'm no longer there", and if the Fast.Move is fast enough, it will still error out with "File already exists". – Jirka Hanika May 31 '18 at 14:16
  • 2
    @v.oddou interestingly, if the file does not exist, File.Delete does indeed work correctly and do nothing. If instead, any of the directories in the path do not exist, you get a DirectoryNotFoundException though. – Brandon Barkley Feb 15 '19 at 16:34
  • 2
    @JirkaHanika you could change if(File.Exists) to while(File.Exists). – Brandon Barkley Feb 15 '19 at 16:34
  • @BrandonBarkley - Correct. Or you could create a one-off FileSystemWatcher and subscribe to its Deleted event. In my opinion the answer should be edited to do one or the other. – Jirka Hanika Feb 20 '19 at 09:31
  • Calling `MoveFileEx` with P/Invoke is a much safer approach. Not only can a Delete followed by a Move still throw an exception (if the file is recreated by another program between the delete and move), but it can also leave the file as missing for an extended period of time. If you are replacing a file, this could cause issues with other programs that expect the file to exist. – yeerk May 24 '19 at 00:41
  • 1
    As of .Net core 3.0 onwards, there is an [easier way](https://stackoverflow.com/a/62055634/3220898). If you are stuck with an older version, I believe [this answer](https://stackoverflow.com/a/42224803/3220898) is less error prone. – Arkane Oct 29 '21 at 12:09
67

You need to move it to another file (rather than a folder), this can also be used to rename.

Move:

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

Rename:

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

The reason it says "File already exists" in your example, is because C:\test\Test tries to create a file Test without an extension, but cannot do so as a folder already exists with the same name.

Lee
  • 1,591
  • 1
  • 14
  • 28
  • 1
    This answer has just helped me to understand why Linux has a syntax of naming a directory with the suffix `.d` where a file with the same name may exist. Thankyou! – Jamie Howarth Sep 13 '21 at 23:31
53

Personally I prefer this method. This will overwrite the file on the destination, removes the source file and also prevent removing the source file when the copy fails.

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
  • 6,845
  • 2
  • 16
  • 14
  • 5
    This is fine for small files (and no requirement for atomic move), but for large files, or cases when you need to be sure you won't end up with duplicates, it's problematic. – River Satya Jun 20 '17 at 23:57
  • Why do you prefer `File.Copy , File.Delete` over `File.Move` ? – John Pietrar Sep 05 '17 at 10:50
  • 10
    File.Move doesnt have an overwrite option. – Mitchell Sep 06 '17 at 11:54
  • 3
    Depending on your use case, this can cause problems. "Move" is a real event in a filesystem watcher. Something listing to filesystem events is going to get a delete and a create event instead of a move event. This will also change the underlying filesystem ID. – Andrew Rondeau Mar 20 '18 at 20:35
  • There is also [Transactional NTFS](https://learn.microsoft.com/en-us/windows/desktop/fileio/about-transactional-ntfs) – Anton Krouglov Oct 17 '18 at 08:13
  • 2
    Won't this be a lot less performant for large files? If the source and destination are on the same physical volume, you're creating a second copy for no reason and then deleting the original, whereas File.Move() will avoid doing extra work if the source and destination are on the same volume. – Brad Westness Mar 19 '19 at 20:36
  • Without Move(overwrite), this is an important "atomic" workaround strategy. Say you download an updated configuration file to disk, and now you need to replace the active one. Such an operation needs to be atomic. If you `Delete` the destination file first then the application crashes, you won't have a valid configuration file to startup with. The same applies, if you are upgrading your application exe. There are many scenarios that need an atomic operation. (Then you delete the excess source file) – Kind Contributor May 28 '20 at 01:50
  • 5
    @Mitchell at .NET v3.0 they've added [overwrite](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.move?view=netcore-3.1#System_IO_File_Move_System_String_System_String_System_Boolean_) parameter. _just for the record_ – Søren Jan 03 '21 at 13:22
  • I've used this solution on Ubuntu Server because the move doesn't delete the source file. It works for me. THX. – Søren Jan 03 '21 at 13:24
21

You can do a P/Invoke to MoveFileEx() - pass 11 for 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);

Or, you can just call

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

after adding Microsoft.VisualBasic as a reference.

mklement0
  • 382,024
  • 64
  • 607
  • 775
mheyman
  • 4,211
  • 37
  • 34
14
  1. With C# on .Net Core 3.0 and beyond, there is now a third boolean parameter:

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.

Source: Microsoft Docs

  1. For all other versions of .Net, this answer is the best. Copy with Overwrite, then delete the source file. This is better because it makes it an atomic operation. (I have attempted to update the MS Docs with this)
Arkane
  • 352
  • 4
  • 10
Kind Contributor
  • 17,547
  • 6
  • 53
  • 70
11

If file really exists and you want to replace it use below code:

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
  • 1,856
  • 2
  • 16
  • 26
4

According to the docs for File.Move there is no "overwrite if exists" parameter. You tried to specify the destination folder, but you have to give the full file specification.

Reading the docs again ("providing the option to specify a new file name"), I think, adding a backslash to the destination folder spec may work.

Ekkehard.Horner
  • 38,498
  • 2
  • 45
  • 96
  • And the docs mention _Note that if you attempt to replace a file by moving a file of the same name into that directory, an IOException is thrown. For that purpose, call `Move(String, String, Boolean)` instead._ but that seems to be a mistake? – Kevin Scharnhorst Oct 24 '19 at 17:35
  • 2
    @KevinScharnhorst This answer was 2011. The documentation now include .Net Core 3.0 support for Move with Overwrite. – Kind Contributor May 28 '20 at 01:53
2

Try Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(Source, Destination, True). The last parameter is Overwrite switch, which System.IO.File.Move doesn't have.

Karlo Kokkak
  • 3,674
  • 4
  • 18
  • 33
Mark
  • 21
  • 1
  • 2
    There is another answer already here that is similar that suggests the same https://stackoverflow.com/a/42224803/1236734 – JG in SD May 18 '18 at 22:09
  • 1
    This is the answer that suggests the same: https://stackoverflow.com/a/38372760/887092, not stackoverflow.com/a/42224803/1236734 – Kind Contributor May 28 '20 at 01:54
2

If you don't have the option to delete the already existing file in the new location, but still need to move and delete from the original location, this renaming trick might work:

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);

This assumes the only '.' in the file name is before the extension. It splits the file in two before the extension, attaches "_copy." in between. This lets you move the file, but creates a copy if the file already exists or a copy of the copy already exists, or a copy of the copy of the copy exists... ;)

Yaw
  • 21
  • 1