7

From the documentation of File.Move:

Note that if you attempt to replace a file by moving a file of the same name into that directory, you get an IOException. You cannot use the Move method to overwrite an existing file.

In short, you can't overwrite on Move, so in order to facilitate overwriting on Move I mimic the behavior by doing a File.Copy followed by a File.Delete. Something like:

if (File.Exists(dstFileName))
{
    // System.IO.File.Move cannot be used to overwrite existing files, so we're going
    // to simulate that behavior with a Copy & Delete.
    File.Copy(procContext.FileName, dstFileName);
    File.Delete(procContext.FileName);
}
else
    File.Move(procContext.FileName, dstFileName);

My question is: Are there any situations that I need to guard against which could lead to the source file being deleted without it first being successfully copied?

My understanding from reading the documentation is that since File.Copy doesn't return anything that it should throw an exception in any case that it doesn't succeed. Has anyone encountered any situations where this isn't true?

M.Babcock
  • 18,753
  • 6
  • 54
  • 84
  • 3
    Wouldn't it be safer to do a delete on the target file if it exists, followed by a move like you did initially? – Tudor Apr 24 '12 at 20:05
  • @Tudor - Not necessarily. If the move fails, then the original destination file will be gone as well which isn't really the expected behavior. – M.Babcock Apr 24 '12 at 20:06
  • But from your question I deduce that if move allowed you to overwrite the file if it exists, you would use it, instead of copy + delete. So I'm assuming you are willing to take that risk? – Tudor Apr 24 '12 at 20:08
  • 1
    Of course I would use `File.Move` if it supported overwriting. However, if it supported overwriting, I would expect it to _rollback_ if the move failed. So in my mind there would be little risk. – M.Babcock Apr 24 '12 at 20:10
  • Ok, but how does this contradict using a delete before executing the move? – Tudor Apr 24 '12 at 20:11
  • I can't undo the deletion of the file if the move fails. – M.Babcock Apr 24 '12 at 20:12
  • 1
    But that may be the answer... rather than deleting the destination, I should instead rename it. – M.Babcock Apr 24 '12 at 20:13
  • I was just about to type that. :)) – Tudor Apr 24 '12 at 20:13
  • Go ahead and post it as an answer and I'll mark it. – M.Babcock Apr 24 '12 at 20:14

5 Answers5

9

I suggest you to probe first if the target file exists and if yes, delete it. Then execute a normal move operation.

Since this sequence is not atomic, in case the destination exists you might want to rename it instead of deleting it, to avoid losing it in case the move fails.

Tudor
  • 61,523
  • 12
  • 102
  • 142
  • 1
    However, this is not an atomic operation. I.e. if we are in the middle of deletion and moving that file and a power failure occurs, we lost the content. This is not hypothetical, but an actual problem we have with one of our applications. See answer below, using File.Replace() instead – Seven Aug 03 '15 at 17:34
  • If, like most people asking this question, you need atomicity, see [Arsen Zahray’s answer](http://stackoverflow.com/a/21882993/429091). – binki Jan 25 '16 at 06:23
7

The correct way to do it would be to call

File.Replace(source, destination, copy)

That does the trick for me

Arsen Zahray
  • 24,367
  • 48
  • 131
  • 224
  • Do you know if File.Replace is copying data under the hood, or just updating file system references (like a move)? More specifically, will it be as fast as a move when source and destination are on the same disk and copy is null? – yoyo Nov 07 '14 at 21:34
  • 1
    Yes, this seems to be an atomic (or at least transactional) operation. See https://social.msdn.microsoft.com/forums/vstudio/en-US/848ce6b7-ce52-43ca-b79b-168835837c63/is-filereplace-atomic-on-an-ntfs-filesystem – Seven Aug 03 '15 at 17:33
  • Oh, so *this* is what `rename()` is called on .net. – binki Jan 24 '16 at 07:23
4

It is difficult to simulate an atomic operation if the operating system doesn't give you good atomic operations. Move is atomic on some but not all filesystems, but not when you are moving disk to disk.

In case of the same disk, Delete + Move is somewhat elegant (fast and safe) as it does not really stuffle the data in any way. You could further extend it to

try
{
    Move(dest, tmp);
    Move(src, dest);
    Delete(tmp);
}
catch
{
    try
    {
        Move(tmp, dest);
    }
    catch
    {
    }
    throw;
}

(This makes it less likely that you will lose the destination file when you for example do not have the rights necessary to finish the move.)

In a scenario where you do not know that it is the same disk, your solution is safe enough and simple enough. However, it copies the data even within the same disk, bringing you a wider window of risk of a power failure.

Jirka Hanika
  • 13,301
  • 3
  • 46
  • 75
2

This is safe. File.Copy will either succeed entirely or throw. Of course, the delete could fail leaving the source file behind as garbage.

If your computer crashes, though, there is no guarantee that the copy oepration has hardened the data yet. You might loose data in that case.

During normal operations this is safe.

usr
  • 168,620
  • 35
  • 240
  • 369
0

Check if file "Target" Exsists. If no, copy your file.

If yes: Move "Target" to temp dir, where you can be sure, that the move will be successful. You can generate a subdir in Temp with the name auf an UUID. Then copy your file.

Tarion
  • 16,283
  • 13
  • 71
  • 107