RXC
RXC

Reputation: 1233

Asynchronous file and folder copy - C#

I have a method to copy all files and folders in one directory to another, and it works recursively. My problem is that it blocks the main thread and I would like to make the actual copying of files and folders asynchronous. I currently am using a function to copy files over asynchronously, but it does not seem to work. Here is the code:

private async void copyEverything(string source, string target)
    {
        // Variable to hold the attributes of a file
        FileAttributes attributes;

        // Get the subdirectories for the specified directory.
        DirectoryInfo dir = new DirectoryInfo(source);
        DirectoryInfo[] dirs = dir.GetDirectories();

        if (!dir.Exists)
        {
            throw new DirectoryNotFoundException(
                "Source directory does not exist or could not be found: " + source);
        }

        // If the destination directory doesn't exist, create it. 
        if (!Directory.Exists(target))
        {
            Directory.CreateDirectory(target);
        }

        // Loop through for all files in a directory
        foreach (string filename in Directory.EnumerateFiles(source))
        {
            if (!File.Exists(targetFolder + filename.Substring(filename.LastIndexOf('\\'))))
            {
                attributes = File.GetAttributes(filename);

                if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                {
                    //System.Windows.MessageBox.Show("File {" + filename + "} is READ ONLY");
                }
                else
                {
                    try
                    {
                        using (FileStream SourceStream = File.Open(filename, FileMode.Open))
                        {
                            using (FileStream DestinationStream = File.Create(target + filename.Substring(filename.LastIndexOf('\\'))))
                            {
                                await SourceStream.CopyToAsync(DestinationStream);
                                filesRemaining--;
                            }
                        }
                    }
                    catch (UnauthorizedAccessException)
                    {
                    }
                }
            }
        }

        // Loop through each subdirectory in the current directory and recursively call the
        // copyEverything() method using the subdirectory's full name and the name of the
        // target folder plus the subdirectory folder name
        foreach (DirectoryInfo subdir in dirs)
        {
            foldersRemaining--;
            string temppath = System.IO.Path.Combine(target, subdir.Name);
            copyEverything(subdir.FullName, temppath);
        }
    }

Is there anything I can do to make it work without blocking the main thread?

Upvotes: 2

Views: 8536

Answers (2)

Justin Pihony
Justin Pihony

Reputation: 67065

Simply marking a method async does not make it async, you need to await the non-UI/blocking code. The await is where your asynchronous code should go, often in a Task.

All code that is not in an await will run in the context of the calling thread (the main in your case). So, even though you are awaiting the copy, you should probably just await the entire block, using a Task as already specified

Upvotes: 0

poy
poy

Reputation: 10507

You're still doing a bunch of IO on the main thread: Directory.Exist, File.Exist, etc... You probably want to avoid doing the entire thing on the main thread.

So, an easy solution would be to add a new method:

private void copyEverythingAsync(string source, string target)
{
  Task.Run(()=> copyEverything(source, target));
}

And then remove the async/await from the copyEverything method.

This will move the operation onto a new thread from the ThreadPool and not block your main thread.

Upvotes: 1

Related Questions