Mizmor
Mizmor

Reputation: 1421

System.IO.File.Move--How to wait for move completion?

I am writing a WPF application in c# and I need to move some files--the rub is that I really REALLY need to know if the files make it. To do this, I wrote a check that makes sure that the file gets to the target directory after the move--the problem is that sometimes I get to the check before the file finishes moving:

   System.IO.File.Move(file.FullName, endLocationWithFile);

            System.IO.FileInfo[] filesInDirectory = endLocation.GetFiles();
            foreach (System.IO.FileInfo temp in filesInDirectory)
            {
                if (temp.Name == shortFileName)
                {

                    return true;
                }
            }

            // The file we sent over has not gotten to the correct   directory....something went wrong!
            throw new IOException("File did not reach destination");

        }
        catch (Exception e)
        {
            //Something went wrong, return a fail;
            logger.writeErrorLog(e);
            return false;
        }

Could somebody tell me how to make sure that the file actually gets to the destination?--The files that I will be moving could be VERY large--(Full HD mp4 files of up to 2 hours)

Thanks!

Upvotes: 12

Views: 18519

Answers (7)

tomasofen
tomasofen

Reputation: 1420

In first place, consider that Moving files in an operating system does not “recreates” the file in the new directory, but only changes its location data in the “files allocation table”, as physically copy all bytes to delete old ones is just a waste of time.

Due to that reason, moving files is a very fast process, no matter the file size.

EDIT: As Mike Christiansen states in his comment, this "speedy" process only happens when files are moving inside the same volume (you know, C:\... to C:\...)

Thus, copy/delete behavior as proposed by “sa_ddam213” in his response will work but is not the optimal solution (takes longer to finish, will not work if for example you don’t have enough free disk to make the copy of the file while the old one exists, …).

MSDN documentation about File.Move(source,destination) method does not specifies if it waits for completion, but the code given as example makes a simple File.Exists(…) check, saying that having there the original file “is unexpected”:

// Move the file.
File.Move(path, path2);
Console.WriteLine("{0} was moved to {1}.", path, path2);
// See if the original exists now.
if (File.Exists(path))
{
    Console.WriteLine("The original file still exists, which is unexpected.");
}
else
{
    Console.WriteLine("The original file no longer exists, which is expected.");
}   

    

Perhaps, you could use a similar approach to this one, checking in a while loop for the existence of the new file, and the non existence of the old one, giving a “timer” exit for the loop just in case something unexpected happens at operating system level, and the files get lost:

// We perform the movement of the file
File.Move(source,destination);
// Sets an "exit" datetime, after wich the loop will end, for example 15 seconds. The moving process should always be quicker than that if files are in the same volume, almost immediate, but not if they are in different ones
DateTime exitDateTime = DateTime.Now.AddSeconds(15);
bool exitLoopByExpiration = false;
// We stops here until copy is finished (by checking fies existence) or the time limit excedes
while (File.Exists(source) && !File.Exists(destination) && !exitLoopByExpiration ) { 
// We compare current datetime with the exit one, to see if we reach the exit time. If so, we set the flag to exit the loop by expiration time, not file moving 
if (DateTime.Now.CompareTo(exitDateTime) > 0) { exitLoopByExpiration = true; }
}
//
if (exitLoopByExpiration) {
// We can perform extra work here, like log problems or throw exception, if the loop exists becouse of time expiration
}

I have checked this solution and seems to work without problems.

Upvotes: 0

Lucas
Lucas

Reputation: 3502

Got similar problem recently.

OnBackupStarts();
//.. do stuff

 new TaskFactory().StartNew(() =>
                {
                    OnBackupStarts()
                    //.. do stuff
                    OnBackupEnds();
                });


void OnBackupEnds()
    {
        if (BackupChanged != null)
        {
            BackupChanged(this, new BackupChangedEventArgs(BackupState.Done));
        }
    }

do not wait, react to event

Upvotes: 0

Yaron Wittenstein
Yaron Wittenstein

Reputation: 41

try checking periodically in a background task whether the copied file size reached the file size of the original file (you can add hashes comparing between the files)

Upvotes: 0

sa_ddam213
sa_ddam213

Reputation: 43606

You could use streams with Aysnc Await to ensure the file is completely copied

Something like this should work:

private void Button_Click(object sender, RoutedEventArgs e)
{
    string sourceFile = @"\\HOMESERVER\Development Backup\Software\Microsoft\en_expression_studio_4_premium_x86_dvd_537029.iso";
    string destinationFile = "G:\\en_expression_studio_4_premium_x86_dvd_537029.iso";

    MoveFile(sourceFile, destinationFile);
}

private async void MoveFile(string sourceFile, string destinationFile)
{
    try
    {
        using (FileStream sourceStream = File.Open(sourceFile, FileMode.Open))
        {
            using (FileStream destinationStream = File.Create(destinationFile))
            {
                await sourceStream.CopyToAsync(destinationStream);
                if (MessageBox.Show("I made it in one piece :), would you like to delete me from the original file?", "Done", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
                {
                    sourceStream.Close();
                    File.Delete(sourceFile);
                }
            }
        }
    }
    catch (IOException ioex)
    {
        MessageBox.Show("An IOException occured during move, " + ioex.Message);
    }
    catch (Exception ex)
    {
        MessageBox.Show("An Exception occured during move, " + ex.Message);
    }
}

If your using VS2010 you will have to install Async CTP to use the new Async/Await syntax

Upvotes: 11

spender
spender

Reputation: 120500

Why not manage the copy yourself by copying streams?

//http://www.dotnetthoughts.net/writing_file_with_non_cache_mode_in_c/
const FileOptions FILE_FLAG_NO_BUFFERING = (FileOptions) 0x20000000;

//experiment with different buffer sizes for optimal speed
var bufLength = 4096;

using(var outFile = 
    new FileStream(
        destPath,
        FileMode.Create, 
        FileAccess.Write, 
        FileShare.None, 
        bufLength, 
        FileOptions.WriteThrough | FILE_FLAG_NO_BUFFERING))
using(var inFile = File.OpenRead(srcPath))
{
    //either
    //inFile.CopyTo(outFile);

    //or
    var fileSizeInBytes = inFile.Length;
    var buf = new byte[bufLength];
    long totalCopied = 0L;
    int amtRead;
    while((amtRead = inFile.Read(buf,0,bufLength)) > 0)
    {
        outFile.Write(buf,0,amtRead);
        totalCopied += amtRead;
        double progressPct = 
            Convert.ToDouble(totalCopied) * 100d / fileSizeInBytes;
        progressPct.Dump();
    }
}
//file is written

Upvotes: 0

Chris
Chris

Reputation: 707

You most likely want the move to happen in a separate thread so that you aren't stopping the execution of your application for hours.

If the program cannot continue without the move being completed, then you could open a dialog and check in on the move thread periodically to update a progress tracker. This provides the user with feedback and will prevent them from feeling as if the program has frozen.

There's info and an example on this here: http://hintdesk.com/c-wpf-copy-files-with-progress-bar-by-copyfileex-api/

Upvotes: 0

Eric J.
Eric J.

Reputation: 150118

You could watch for the files to disappear from the original directory, and then confirm that they indeed appeared in the target directory.

I have not had great experience with file watchers. I would probably have the thread doing the move wait for an AutoResetEvent while a separate thread or timer runs to periodically check for the files to disappear from the original location, check that they are in the new location, and perhaps (depending on your environment and needs) perform a consistency check (e.g. MD5 check) of the files. Once those conditions are satisfied, the "checker" thread/timer would trigger the AutoResetEvent so that the original thread can progress.

Include some "this is taking way too long" logic in the "checker".

Upvotes: 1

Related Questions