Copy the entire contents of a directory in C#
Asked Answered
T

29

629

I want to copy the entire contents of a directory from one location to another in C#.

There doesn't appear to be a way to do this using System.IO classes without lots of recursion.

There is a method in VB that we can use if we add a reference to Microsoft.VisualBasic:

new Microsoft.VisualBasic.Devices.Computer().
    FileSystem.CopyDirectory( sourceFolder, outputFolder );

This seems like a rather ugly hack. Is there a better way?

Thrips answered 12/9, 2008 at 11:38 Comment(22)
Copy Directory and its Content to Another Directory in C#.Washhouse
I would say that looking at the alternatives posted below, that the VB way doesn't look so ugly.Strychninism
The real question is, why isn't this in the default IO library? By now we probably all have put the same code in our own personal library.Ceraceous
How can it be a hack when it is part of the .NET Framework? Stop writing code and use what you got.Doghouse
Microsoft.VisualBasic is a bunch of add on stuff for making legacy VB6 projects easier to upgrade. You wouldn't normally use it in a C# application. If it was a 'proper' part of the .Net framework it would be in System.IO. Also only the System.[something] namespaces are part of Mono.Thrips
That is a common misconception. Microsft.VisualBasic contains all the common Visual Basic procedures that makes coding in VB so much easier. Microsot.VisualBasic.Compatibility is the assembly used for VB6 legacy.Doghouse
If you look at the source for CopyDirectory you will see that it uses System.IO or internal Shell API calls, depending on how CopyDirectory is called.Doghouse
The reason why Microsoft.VisualBasic is not added to a C# project is because it is not a VB project. Even VB.NET projects must add Microsoft.VisualBasic.Compatibility if they want to use legacy features. The compatibility layer is only added by the migration wizard or by the user.Doghouse
There is over 2,000 lines of code to Microsoft.VisualBasic.Devices.Computer.FileSystem. CopyDirectory ensures you are not copying a parent folder into a child folder and other checks. It is highly optimized, and so on. The selected answer is fragile code at best.Doghouse
That is a limitation of Mono. Not part of your answer. You want a better way and there is none.Doghouse
If you need System.Windows.Forms.Design, are you going to avoid adding that reference just because it has the words "Design"? Of course not. Therefore, avoiding something that is built into the framework just because it has the word "VisualBasic" in its name is, well..., just plain...silly.Doghouse
@Doghouse - ok, so why is this optimised and complete code in Microsoft.VisualBasic and not System.IO? The reason it isn't in Mono is because all the libraries that are considered 'core' are System.[something] - all the other ones are not. I've got no problem referencing an extra DLL, but there's a good reason why Microsoft haven't included this feature in System.IO.Thrips
To all of you who think it just fine to use Microsoft.VisualBasic: would you be happy using a library from Perl in python? That's basically what's happening, with minor differences. Also, by not using System.* libraries, they are potentially constraining themselves from using Mono, which i gather may be a problem from the comments made by the OP.Seismology
@RCIX: The best developers are the ones that get the job done effectively and quickly. So what if the name has Visual Basic in it, the fact is, the code in the DLL is merely MSIL. Microsoft has clearly written an effective algorithm and it would be a shame for someone to overlook it just because it has the word Visual Basic in its name. Especially so if an algorithm he writes just because of a DLL name oddity ends up being buggy and costs more time to fix.Frosted
@Keith: Perhaps the BCL was finalized prior to them discovering that this function was needed. All we can do is speculate at this point. Do you have a good reason why you shouldn't include the DLL other than the name? Does the MSDN documentation say that this function is deprecated?Frosted
@jasonh: nah - this is quite an old question now and the Microsoft.VisualBasic reference has been in shipped software for over a year. I just wondered why it was in the strange location - it should be something like System.IO.Directory.Copy(sourceFolder, outputFolder)Thrips
I agree that's where it should be, but it's definitely plausible that the BCL was finalized before they realized it would be necessary for VB and thus, the utility of it and they never thought to revisit it. Just theorizing mind you. :)Frosted
I just Q&A'd this with some xcopy like options in C#. #22152495Uptotheminute
Note that Microsoft.VisualBasic.Devices is not available in .NET core: github.com/dotnet/docs/issues/14546Korrie
Using this code in production to copy nested directories and files. Didn't find any issue. learn.microsoft.com/en-us/dotnet/standard/io/…Index
Give this an upvote: github.com/microsoft/dotnet/issues/794Mayan
@juFo, sorry, I really don't think we need it now, I've responded to the GH issue with detail.Thrips
P
662

Much easier

private static void CopyFilesRecursively(string sourcePath, string targetPath)
{
    //Now Create all of the directories
    foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
    {
        Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath));
    }

    //Copy all the files & Replaces any files with the same name
    foreach (string newPath in Directory.GetFiles(sourcePath, "*.*",SearchOption.AllDirectories))
    {
        File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true);
    }
}
Pug answered 29/9, 2010 at 15:3 Comment(20)
Neat idea - I don't know why I never thought of using SearchOption.AllDirectories. I'd probably use the SubString method rather than Replace, but that's just coding style stuff.Thrips
It's a nice piece of code indeed but this is not the kind of code that can be used anywhere. Developers should be careful because dirPath.Replace could cause unwanted consequences. Just a warning to people that like doing copy and paste over the net. The code posted by @jaysponsored is safer because it doesn't use string.Replace but I'm sure it also has its corner cases.Fink
Awesome! Its resolve my issue. But is there any way to copy/move files with billion of numbers and without removing existing files/folders ?Darkness
Be careful with this code as it will throw an exception if the target directory exists already. It will also not overwrite files that already exists. Simply add a check before creating each directory and use the overload of File.Copy to overwrite target file if exists.Alberic
@Fink - What makes String.Substring better than String.Replace in this case?Heredes
@Fink - that's exactly why I'd use the SubString method rather than ReplaceThrips
@Heredes - Replace has a problem if you have a repeating pattern inside the path, for instance "sourceDir/things/sourceDir/things" should become "destinationDir/things/sourceDir/things", but if you use replace it becomes "destinationDir/things/destinationDir/things"Thrips
@Rick this method does File.Copy so it leaves the original. If you wanted to remove the original you could use File.Move or (probably better) add a loop to delete the files after the copy is complete.Thrips
Why *.* instead of *? Don't you want to copy files without extensions too?Postman
Should also note that the paths require a backslash at the end. Without, the file and folder names will get appended to the destination root name for each item. Just a tip!Sharpfreeze
@AaronLS the AllDirectories option means the files may be in sub-directories of the source directory. Using your code will cause all files to be copied into the target directory itself rather than into a sub-directory of the target directory.Jiggle
@Alex: what's the problem with dirPath.Replace ??? Could you elaborate on the issues with this code assuming that Source and Destination paths are rooted?Neritic
Nice answer, but one remark: Isn't newPath variable name a little bit misleading? Shouldn't it be called rather sourcePath or sourceFilePath?Java
What if we only want to files from directory and sub directories. and dont want directories? Any one ?Clouded
@Thrips the chances of that are small, but i still agree that substring is a more correct way then to call replaceCountless
This fails when the directory contains any junction link or symbolic link. Is there any way to overcome that?Thionate
The Directory.GetDirectories("*.*", System.IO.SearchOption.AllDirectories) will fail if any one of the subdirectories under the specified root causes a DirectoryNotFoundException or UnauthorizedAccessException, the whole method fails and returns no directories. Can you suggest a method that will take into consideration when the above mention exceptions are thrownAccessory
This doesn't work for me on Linux. Just use the recursive example outlined by Microsoft at https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directoriesBoyt
It's great. I'll add these lines at start of this method to enhance it: if (!sourcePath.EndsWith(@"\")) sourcePath += @"\"; if (!targetPath.EndsWith(@"\")) targetPath += @"\"; if (!Directory.Exists(targetPath)) Directory.CreateDirectory(targetPath); The last "if" corrects a bug where I want to copy contents from a folder where there's no subfolder in it, and my target path doesn't exist (i'm creating it from string in targetPath).Elul
In my last comment, the first 2 "ifs" corrects a bug that happened to me where it appended the folder from target to each filename being copied. StackOverflow didn't allow me to edit my comment after 5 minutes...Elul
O
274

Hmm, I think I misunderstand the question but I'm going to risk it. What's wrong with the following straightforward method?

public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) {
    foreach (DirectoryInfo dir in source.GetDirectories())
        CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
    foreach (FileInfo file in source.GetFiles())
        file.CopyTo(Path.Combine(target.FullName, file.Name));
}

EDIT Since this posting has garnered an impressive number of downvotes for such a simple answer to an equally simple question, let me add an explanation. Please read this before downvoting.

First of all, this code is not intendend as a drop-in replacement to the code in the question. It is for illustration purpose only.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectory does some additional correctness tests (e.g. whether the source and target are valid directories, whether the source is a parent of the target etc.) that are missing from this answer. That code is probably also more optimized.

That said, the code works well. It has (almost identically) been used in a mature software for years. Apart from the inherent fickleness present with all IO handlings (e.g. what happens if the user manually unplugs the USB drive while your code is writing to it?), there are no known problems.

In particular, I’d like to point out that the use of recursion here is absolutely not a problem. Neither in theory (conceptually, it’s the most elegant solution) nor in practice: this code will not overflow the stack. The stack is large enough to handle even deeply nested file hierarchies. Long before stack space becomes a problem, the folder path length limitation kicks in.

Notice that a malicious user might be able to break this assumption by using deeply-nested directories of one letter each. I haven’t tried this. But just to illustrate the point: in order to make this code overflow on a typical computer, the directories would have to be nested a few thousand times. This is simply not a realistic scenario.

Overstuffed answered 12/9, 2008 at 12:1 Comment(12)
Nowt, 'cept the recursive call. Why do we need to do that in C#?Thrips
This is head recursion. It can fall prey to a stack overflow if the directories are nested deep enough.Zacynthus
Until very recently, directory nesting depth was restricted by the OS. I doubt that you'll find directories that are nested more than a few hundred times (if even). The above code can take much more.Overstuffed
I like the recursive approach, the risk of a stack overflow is minimum at worst.Tubulate
@DTashkinov: well excuse me but that seems a tad excessive. Why is obvious code == downvote? The opposite should be true. The built-in method had already been posted but Keith asked specifically for another method. Also, what do you mean by your last sentence? Sorry, but I just don't understand your reasons for downvoting at all.Overstuffed
Sorry if don't understand the rules of upvoting and downvoting. The posted code itself looks ok. What I wanted to say is writing your own code for trivial tasks is the first temptation to come up when facing them, but the last thing to do. Personnaly I visited this page trying to find a built-in solution and was dissapointed.Mcgary
This is not a better way. Period. Use the debugged and production ready code that Microsoft provided in the Framework.Doghouse
@AMissico: better than what? Nobody claimed it to be better than the VB code from the framework. We know it isn’t.Overstuffed
Seems a reasonable approach IF you don't want to use the VB code mentioned in the question. I don't understand the objections to recursion. That code is never going to blow the stack unless you hit a situation with a symbolic link (aka reparse point) back to a parent directory and it's easy enough to avoid following those and also count recursion depth to protect against other weird loops. Microsoft recommend recursion in their HowTo answer: msdn.microsoft.com/en-us/library/vstudio/…Saransarangi
Upvoted, and added an answer which is a variation of this, where the source itself becomes a folder under target, then it's children go under that.Akin
I'm not sure exactly why or how, but when I tried, the code would sometimes fail by trying to copy files before the directory is actually made - I had to add Directory.CreateDirectory(target.FullName); to the second if statement for the code to always work. Love the recursive code though, great solutionSchoenfeld
@TheLemon The code currently assumes that the top-level target directory already exists. If the code called with a non-existent target, it will fail (and adding a check for this might be a good idea!). However, subdirectories are correctly created: that’s what the target.CreateSubdirectory call does.Overstuffed
F
167

Copied from MSDN:

using System;
using System.IO;

class CopyDir
{
    public static void Copy(string sourceDirectory, string targetDirectory)
    {
        DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
        DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);

        CopyAll(diSource, diTarget);
    }

    public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
    {
        Directory.CreateDirectory(target.FullName);

        // Copy each file into the new directory.
        foreach (FileInfo fi in source.GetFiles())
        {
            Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);
            fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
        }

        // Copy each subdirectory using recursion.
        foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
        {
            DirectoryInfo nextTargetSubDir =
                target.CreateSubdirectory(diSourceSubDir.Name);
            CopyAll(diSourceSubDir, nextTargetSubDir);
        }
    }

    public static void Main()
    {
        string sourceDirectory = @"c:\sourceDirectory";
        string targetDirectory = @"c:\targetDirectory";

        Copy(sourceDirectory, targetDirectory);
    }

    // Output will vary based on the contents of the source directory.
}
Foliar answered 27/3, 2009 at 18:35 Comment(4)
There's no reason to check if the directory exists, simply call Directoty.CreateDirectory which will do nothing if the directory already exists.Blackmarket
For those looking to deal with paths greater than 256 characters, you can use a Nuget package called ZetaLongPathsExalted
This answer seems to be the most useful of them all. By using DirectoryInfo instead of strings a lot of potential problems are avoided.Percentile
Why the one is called Copy and the other CopyAll if they both do the same thing? Based solely on names I would assume the Copy would not copy "all" the contents. Didn't C# have overloads back in 2009?Convexoconcave
K
61

Or, if you want to go the hard way, add a reference to your project for Microsoft.VisualBasic and then use the following:

Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(fromDirectory, toDirectory);

However, using one of the recursive functions is a better way to go since it won't have to load the VB dll.

Kumar answered 12/9, 2008 at 11:46 Comment(4)
That isn't really different from how I did it anyway - you still need to load VB's backward-compatibility stuff in order to be able to do it.Thrips
Is loading the VB assembly expensive? The VB options are much more elegant than the C# versions.Arathorn
What "VB's backward-compatibility stuff"? CopyDirectory uses either the Shell or the Framework.Doghouse
I do wish it was on System.IO.Directory, but it's better than rewriting it!Booma
A
54

Try this:

Process proc = new Process();
proc.StartInfo.UseShellExecute = true;
proc.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "xcopy.exe");
proc.StartInfo.Arguments = @"C:\source C:\destination /E /I";
proc.Start();

Your xcopy arguments may vary but you get the idea.

Amerson answered 12/9, 2008 at 12:30 Comment(11)
what do the /E /I stand for? Overwrite?Hagiography
/E tells it to copy all sub directories (even empty ones). /I tells it that if the destination doesn't exist create a directory with that name.Amerson
add double quote to be safe.Usm
Add /Y to prevent getting prompted to overwrite existing files. https://mcmap.net/q/21260/-how-to-say-no-to-all-quot-do-you-want-to-overwrite-quot-prompts-in-a-batch-file-copy/138938Dottie
Adding the /d option is useful to only copy modified files, along with the /i optionMethane
I know I'm getting here late, but I was looking for a solution to this problem as well. I think you should have /S as well if you're going to use /E: "Use /e with the /s and /t command-line options" (technet.microsoft.com/en-us/library/bb491035.aspx)Tenia
If cross-platform support is not needed, this is hands down the best approach. I'd use robocopy though, e.g. Robocopy C:\A C:\B /EKorrie
Sorry, but this is horrible. It assumes that the target system is windows. It assumes that future versions include xcopy.exe at that specific path. It assumes that the parameters of xcopy do not change. It requires to assemble the parameters for xcopy as string, which introduces plenty of error potential. Also the sample does not mention any error handling for the results of the started process, which i would expect, because contrary to other methods this would fail silently.Pantograph
@MatthiasJansen, I think you took it very personal. The answer is to the point and explains much about how to achieve it... Since the question doesnt demand the cross platform compatibility or not using xcopy or anything else the poster just answered to explain how this can be achieved one way... There might be 1000 ways to do same thing and the answers vary.. that's why this forum is here to address and programmers around the globe come here to share their experiences. I down vote your comment.Dugout
It worked perfectly - thank you for posting. I needed a quick and dirty solution for a utility I'm writing. I didn't want a lot of code.Strunk
what are better or faster, xcopy.exe or System.IO ?Clein
D
45

This site always have helped me out a lot, and now it's my turn to help the others with what I know.

I hope that my code below be useful for someone.

string source_dir = @"E:\";
string destination_dir = @"C:\";

// substring is to remove destination_dir absolute path (E:\).

// Create subdirectory structure in destination    
    foreach (string dir in System.IO.Directory.GetDirectories(source_dir, "*", System.IO.SearchOption.AllDirectories))
    {
        System.IO.Directory.CreateDirectory(System.IO.Path.Combine(destination_dir, dir.Substring(source_dir.Length + 1)));
        // Example:
        //     > C:\sources (and not C:\E:\sources)
    }

    foreach (string file_name in System.IO.Directory.GetFiles(source_dir, "*", System.IO.SearchOption.AllDirectories))
    {
        System.IO.File.Copy(file_name, System.IO.Path.Combine(destination_dir, file_name.Substring(source_dir.Length + 1)));
    }
Dachi answered 5/11, 2011 at 17:43 Comment(10)
Remember about the trailing backslashObaza
Folks, use Path.Combine(). Never use string concatenation to put file paths together.Antoninus
You have an OBOB in the above code snippet. You should be using source_dir.Length + 1, not source_dir.Length.Nummular
This code is a good concept, but... A file doesn't have to have a "." in it, so it would be better to use ystem.IO.Directory.GetFiles(source_dir, "*", System.IO.SearchOption.AllDirectories))Gotha
Thank you @JeanLibera, you're right. I changed the code with your suggestion.Dachi
replace 'file_name.Substring(..)' with 'Path.GetFileName(file_name)'Salas
I believe that the replacement will not work @Salas because the substring functions remove the source_dir part from file name, but keep it's full path. Example: you're copying a file from D:\sources\install.wim to E:\. With the substring function, it will became E:\sources\install.wim, but if I use GetFileName it will be E:\install.wim. With recursion, the mess is even worse.Dachi
yes, is not valid GetFileName. Best solution is Substring, replace from previous answer is not safe. ThanksSalas
Best answer, but you should use source_dir.Length + 1 as mentioned or TrimStart('\\'))Randi
Nice. Initially my folders would be created with the first letter missing Eg: ongs instead of Songs. Then I removed the slash in source_dir = @"E:\"; and made it source_dir = @"E:";. Then it worked in a jiffy !!!Sisterly
B
16

Copy folder recursively without recursion to avoid stack overflow.

public static void CopyDirectory(string source, string target)
{
    var stack = new Stack<Folders>();
    stack.Push(new Folders(source, target));

    while (stack.Count > 0)
    {
        var folders = stack.Pop();
        Directory.CreateDirectory(folders.Target);
        foreach (var file in Directory.GetFiles(folders.Source, "*.*"))
        {
            File.Copy(file, Path.Combine(folders.Target, Path.GetFileName(file)));
        }

        foreach (var folder in Directory.GetDirectories(folders.Source))
        {
            stack.Push(new Folders(folder, Path.Combine(folders.Target, Path.GetFileName(folder))));
        }
    }
}

public class Folders
{
    public string Source { get; private set; }
    public string Target { get; private set; }

    public Folders(string source, string target)
    {
        Source = source;
        Target = target;
    }
}
Bybee answered 27/3, 2010 at 0:47 Comment(1)
Hard to imagine blowing the stack before glowing the path limitConvergent
A
5

Here's a utility class I've used for IO tasks like this.

using System;
using System.Runtime.InteropServices;

namespace MyNameSpace
{
    public class ShellFileOperation
    {
        private static String StringArrayToMultiString(String[] stringArray)
        {
            String multiString = "";

            if (stringArray == null)
                return "";

            for (int i=0 ; i<stringArray.Length ; i++)
                multiString += stringArray[i] + '\0';

            multiString += '\0';

            return multiString;
        }

        public static bool Copy(string source, string dest)
        {
            return Copy(new String[] { source }, new String[] { dest });
        }

        public static bool Copy(String[] source, String[] dest)
        {
            Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();

            FileOpStruct.hwnd = IntPtr.Zero;
            FileOpStruct.wFunc = (uint)Win32.FO_COPY;

            String multiSource = StringArrayToMultiString(source);
            String multiDest = StringArrayToMultiString(dest);
            FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
            FileOpStruct.pTo = Marshal.StringToHGlobalUni(multiDest);

            FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION;
            FileOpStruct.lpszProgressTitle = "";
            FileOpStruct.fAnyOperationsAborted = 0;
            FileOpStruct.hNameMappings = IntPtr.Zero;

            int retval = Win32.SHFileOperation(ref FileOpStruct);

            if(retval != 0) return false;
            return true;
        }

        public static bool Move(string source, string dest)
        {
            return Move(new String[] { source }, new String[] { dest });
        }

        public static bool Delete(string file)
        {
            Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();

            FileOpStruct.hwnd = IntPtr.Zero;
            FileOpStruct.wFunc = (uint)Win32.FO_DELETE;

            String multiSource = StringArrayToMultiString(new string[] { file });
            FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
            FileOpStruct.pTo =  IntPtr.Zero;

            FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_SILENT | (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION | (ushort)Win32.ShellFileOperationFlags.FOF_NOERRORUI | (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMMKDIR;
            FileOpStruct.lpszProgressTitle = "";
            FileOpStruct.fAnyOperationsAborted = 0;
            FileOpStruct.hNameMappings = IntPtr.Zero;

            int retval = Win32.SHFileOperation(ref FileOpStruct);

            if(retval != 0) return false;
            return true;
        }

        public static bool Move(String[] source, String[] dest)
        {
            Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();

            FileOpStruct.hwnd = IntPtr.Zero;
            FileOpStruct.wFunc = (uint)Win32.FO_MOVE;

            String multiSource = StringArrayToMultiString(source);
            String multiDest = StringArrayToMultiString(dest);
            FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
            FileOpStruct.pTo = Marshal.StringToHGlobalUni(multiDest);

            FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION;
            FileOpStruct.lpszProgressTitle = "";
            FileOpStruct.fAnyOperationsAborted = 0;
            FileOpStruct.hNameMappings = IntPtr.Zero;

            int retval = Win32.SHFileOperation(ref FileOpStruct);

            if(retval != 0) return false;
            return true;
        }
    }
}
Aubigny answered 12/9, 2008 at 11:57 Comment(1)
Note that Microsoft uses SHFileOperation internally for Microsoft.VisualBasic.Enlil
P
4

tboswell 's replace Proof version (which is resilient to repeating pattern in filepath)

public static void copyAll(string SourcePath , string DestinationPath )
{
   //Now Create all of the directories
   foreach (string dirPath in Directory.GetDirectories(SourcePath, "*", SearchOption.AllDirectories))
      Directory.CreateDirectory(Path.Combine(DestinationPath ,dirPath.Remove(0, SourcePath.Length ))  );

   //Copy all the files & Replaces any files with the same name
   foreach (string newPath in Directory.GetFiles(SourcePath, "*.*",  SearchOption.AllDirectories))
      File.Copy(newPath, Path.Combine(DestinationPath , newPath.Remove(0, SourcePath.Length)) , true);
    }
Proteus answered 2/3, 2016 at 8:39 Comment(1)
In my case, for the directories, I had to use Path.Join() instead of Path.Combine(). I don't fully understand why though, but I guess I was doing something related to this remark in the documentation, which recommends Path.Join()Lulita
O
4

My solution is basically a modification of @Termininja's answer, however I have enhanced it a bit and it appears to be more than 5 times faster than the accepted answer.

public static void CopyEntireDirectory(string path, string newPath)
{
    Parallel.ForEach(Directory.GetFileSystemEntries(path, "*", SearchOption.AllDirectories)
    ,(fileName) =>
    {
        string output = Regex.Replace(fileName, "^" + Regex.Escape(path), newPath);
        if (File.Exists(fileName))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(output));
            File.Copy(fileName, output, true);
        }
        else
            Directory.CreateDirectory(output);
    });
}

EDIT: Modifying @Ahmed Sabry to full parallel foreach does produce a better result, however the code uses recursive function and its not ideal in some situation.

public static void CopyEntireDirectory(DirectoryInfo source, DirectoryInfo target, bool overwiteFiles = true)
{
    if (!source.Exists) return;
    if (!target.Exists) target.Create();

    Parallel.ForEach(source.GetDirectories(), (sourceChildDirectory) =>
        CopyEntireDirectory(sourceChildDirectory, new DirectoryInfo(Path.Combine(target.FullName, sourceChildDirectory.Name))));

    Parallel.ForEach(source.GetFiles(), sourceFile =>
        sourceFile.CopyTo(Path.Combine(target.FullName, sourceFile.Name), overwiteFiles));
}
Overestimate answered 21/11, 2018 at 4:24 Comment(0)
H
4

Here is a concise and efficient solution:

namespace System.IO {
  public static class ExtensionMethods {

    public static void CopyTo(this DirectoryInfo srcPath, string destPath) {
      Directory.CreateDirectory(destPath);
      Parallel.ForEach(srcPath.GetDirectories("*", SearchOption.AllDirectories), 
        srcInfo => Directory.CreateDirectory($"{destPath}{srcInfo.FullName[srcPath.FullName.Length..]}"));
      Parallel.ForEach(srcPath.GetFiles("*", SearchOption.AllDirectories), 
        srcInfo => File.Copy(srcInfo.FullName, $"{destPath}{srcInfo.FullName[srcPath.FullName.Length..]}", true));
      });
    }

  }
}

To use:

new DirectoryInfo(sourcePath).CopyTo(destinationPath);
Highspeed answered 6/9, 2021 at 6:28 Comment(0)
S
3

A minor improvement on d4nt's answer, as you probably want to check for errors and not have to change xcopy paths if you're working on a server and development machine:

public void CopyFolder(string source, string destination)
{
    string xcopyPath = Environment.GetEnvironmentVariable("WINDIR") + @"\System32\xcopy.exe";
    ProcessStartInfo info = new ProcessStartInfo(xcopyPath);
    info.UseShellExecute = false;
    info.RedirectStandardOutput = true;
    info.Arguments = string.Format("\"{0}\" \"{1}\" /E /I", source, destination);

    Process process = Process.Start(info);
    process.WaitForExit();
    string result = process.StandardOutput.ReadToEnd();

    if (process.ExitCode != 0)
    {
        // Or your own custom exception, or just return false if you prefer.
        throw new InvalidOperationException(string.Format("Failed to copy {0} to {1}: {2}", source, destination, result));
    }
}
Sum answered 3/2, 2012 at 10:51 Comment(0)
V
3

It may not be performance-aware, but I'm using it for 30MB folders and it works flawlessly. Plus, I didn't like all the amount of code and recursion required for such an easy task.

var src = "c:\src";
var dest = "c:\dest";
var cmp = CompressionLevel.NoCompression;
var zip = source_folder + ".zip";

ZipFile.CreateFromDirectory(src, zip, cmp, includeBaseDirectory: false);
ZipFile.ExtractToDirectory(zip, dest_folder);

File.Delete(zip);

Note: ZipFile is available on .NET 4.5+ in the System.IO.Compression namespace

Vtol answered 17/7, 2018 at 8:14 Comment(6)
Neither do I, hence the question, but the selected answer doesn't need recursion. This answer creates a zip file on disk, which is a lot of additional work for a file copy - not only are you creating an additional copy of the data, but you're spending processor time compressing and decompressing it. I'm sure it works, the same way you can probably knock a nail in with your shoe, but it's more work with more things that can go wrong, while there are better ways of doing it.Thrips
The reason I ended up with this is string replacements. As others have pointed out, the accepted answer presents many concerns; junction link may not work, as well as repeating folder pattern or files without extension or name. Less code, less chance to go wrong. And since processor time is not a concern for me, it makes it suitable for my specific caseVtol
Yeah, that's like driving 1000 miles out of your way to avoid a single traffic light, but it's your journey, so go for it. Checking for folder patterns is trivial compared to what ZIP needs to do under the hood. I'd strongly recommend against this for anyone who cares about not wasting processor, disk, electricity or where this needs to run alongside other programs on the same machine. Also, if you're ever asked this type of question at interview never go with "my code is simple so I don't care about processor time" - you won't get the job.Thrips
I switched to the answer provided by @justin-r. Still, I'll leave this answer there as just another way of doing itVtol
If the folders are on separate network shares and contain a lot of files, this would be the best option in my opinion.Bowrah
Very creative! In terms of correctness this should probably be the #1 answer, I'm sure it handles a 1000 edge cases other naïve answers miss. Alternatively execute robocopy.exe (if x-plat is not an issue).Korrie
L
2

This is my code hope this help

private void KCOPY(string source, string destination)
{
    if (IsFile(source))
    {
        string target = Path.Combine(destination, Path.GetFileName(source));
        File.Copy(source, target, true);
    }
    else
    {
        string fileName = Path.GetFileName(source);
        string target = System.IO.Path.Combine(destination, fileName);
        if (!System.IO.Directory.Exists(target))
        {
            System.IO.Directory.CreateDirectory(target);
        }

        List<string> files = GetAllFileAndFolder(source);

        foreach (string file in files)
        {
            KCOPY(file, target);
        }
    }
}

private List<string> GetAllFileAndFolder(string path)
{
    List<string> allFile = new List<string>();
    foreach (string dir in Directory.GetDirectories(path))
    {
        allFile.Add(dir);
    }
    foreach (string file in Directory.GetFiles(path))
    {
        allFile.Add(file);
    }

    return allFile;
}
private bool IsFile(string path)
{
    if ((File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory)
    {
        return false;
    }
    return true;
}
Lanlana answered 22/9, 2012 at 10:38 Comment(1)
See the selected answer, by using the SearchOption flag on the searches for folders and files it does this in 4 lines of code. Also check out the .HasFlag extension now on enums.Thrips
A
2

If you like Konrad's popular answer, but you want the source itself to be a folder under target, rather than putting it's children under the target folder, here's the code for that. It returns the newly created DirectoryInfo, which is handy:

public static DirectoryInfo CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
{
  var newDirectoryInfo = target.CreateSubdirectory(source.Name);
  foreach (var fileInfo in source.GetFiles())
    fileInfo.CopyTo(Path.Combine(newDirectoryInfo.FullName, fileInfo.Name));

  foreach (var childDirectoryInfo in source.GetDirectories())
    CopyFilesRecursively(childDirectoryInfo, newDirectoryInfo);

  return newDirectoryInfo;
}
Akin answered 5/4, 2015 at 22:38 Comment(0)
G
2

You can always use this, taken from Microsofts website.

static void Main()
{
    // Copy from the current directory, include subdirectories.
    DirectoryCopy(".", @".\temp", true);
}

private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
{
    // Get the subdirectories for the specified directory.
    DirectoryInfo dir = new DirectoryInfo(sourceDirName);

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

    DirectoryInfo[] dirs = dir.GetDirectories();
    // If the destination directory doesn't exist, create it.
    if (!Directory.Exists(destDirName))
    {
        Directory.CreateDirectory(destDirName);
    }

    // Get the files in the directory and copy them to the new location.
    FileInfo[] files = dir.GetFiles();
    foreach (FileInfo file in files)
    {
        string temppath = Path.Combine(destDirName, file.Name);
        file.CopyTo(temppath, false);
    }

    // If copying subdirectories, copy them and their contents to new location.
    if (copySubDirs)
    {
        foreach (DirectoryInfo subdir in dirs)
        {
            string temppath = Path.Combine(destDirName, subdir.Name);
            DirectoryCopy(subdir.FullName, temppath, copySubDirs);
        }
    }
}
Gunstock answered 19/7, 2017 at 19:3 Comment(1)
This is great -- Keep in mind the line file.CopyTo(temppath, false); says "copy this file to this place, only if it doesn't exist", which most of the time isn't what we want. But, i can understand why it defaults to that. Maybe add a flag to the method for overwriting files.Antoninus
M
1

Sorry for the previous code, it still had bugs :( (fell prey to the fastest gun problem) . Here it is tested and working. The key is the SearchOption.AllDirectories, which eliminates the need for explicit recursion.

string path = "C:\\a";
string[] dirs = Directory.GetDirectories(path, "*.*", SearchOption.AllDirectories);
string newpath = "C:\\x";
try
{
    Directory.CreateDirectory(newpath);
}
catch (IOException ex)
{
    Console.WriteLine(ex.Message);
}
for (int j = 0; j < dirs.Length; j++)
{
    try
    {
        Directory.CreateDirectory(dirs[j].Replace(path, newpath));
    }
    catch (IOException ex)
    {
        Console.WriteLine(ex.Message);
    }
}

string[] files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
for (int j = 0; j < files.Length; j++)            
{
    try
    {
        File.Copy(files[j], files[j].Replace(path, newpath));
    }
    catch (IOException ex)
    {
        Console.WriteLine(ex.Message);
    }
}
Mosstrooper answered 12/9, 2008 at 11:49 Comment(0)
P
1

Here is an extension method for DirectoryInfo a la FileInfo.CopyTo (note the overwrite parameter):

public static DirectoryInfo CopyTo(this DirectoryInfo sourceDir, string destinationPath, bool overwrite = false)
{
    var sourcePath = sourceDir.FullName;

    var destination = new DirectoryInfo(destinationPath);

    destination.Create();

    foreach (var sourceSubDirPath in Directory.EnumerateDirectories(sourcePath, "*", SearchOption.AllDirectories))
        Directory.CreateDirectory(sourceSubDirPath.Replace(sourcePath, destinationPath));

    foreach (var file in Directory.EnumerateFiles(sourcePath, "*", SearchOption.AllDirectories))
        File.Copy(file, file.Replace(sourcePath, destinationPath), overwrite);

    return destination;
}
Postman answered 26/3, 2013 at 22:7 Comment(0)
G
1

Use this class.

public static class Extensions
{
    public static void CopyTo(this DirectoryInfo source, DirectoryInfo target, bool overwiteFiles = true)
    {
        if (!source.Exists) return;
        if (!target.Exists) target.Create();

        Parallel.ForEach(source.GetDirectories(), (sourceChildDirectory) => 
            CopyTo(sourceChildDirectory, new DirectoryInfo(Path.Combine(target.FullName, sourceChildDirectory.Name))));

        foreach (var sourceFile in source.GetFiles())
            sourceFile.CopyTo(Path.Combine(target.FullName, sourceFile.Name), overwiteFiles);
    }
    public static void CopyTo(this DirectoryInfo source, string target, bool overwiteFiles = true)
    {
        CopyTo(source, new DirectoryInfo(target), overwiteFiles);
    }
}
Gladis answered 10/8, 2017 at 13:2 Comment(1)
This is similar to other answers, refactored to use .ToList().ForEach( (which is slightly more work, memory and slightly slower than just enumerating the directories directly) and as an extension method. The selected answer uses SearchOption.AllDirectories and avoids recursion, so I'd recommend switching to that model. Also, you usually don't need the name of the type in extension methods - I'd rename it to CopyTo() so that it became sourceDir.CopyTo(destination);Thrips
N
1

One variant with only one loop for copying of all folders and files:

foreach (var f in Directory.GetFileSystemEntries(path, "*", SearchOption.AllDirectories))
{
    var output = Regex.Replace(f, @"^" + path, newPath);
    if (File.Exists(f)) File.Copy(f, output, true);
    else Directory.CreateDirectory(output);
}
Natalee answered 24/3, 2018 at 6:31 Comment(1)
If you're going to use Regex, you should probably also Regex.Escape(path) as part of your expression composition (especially considering the Windows path separator). You might also get benefit from creating (and maybe compiling) a new Regex() object outside of the loop, rather than relying on the static method.Tolerate
C
1
public static class Extensions
{
    public static void Copy(this DirectoryInfo self, DirectoryInfo destination, bool recursively)
    {
        foreach (var file in self.GetFiles())
        {
            file.CopyTo(Path.Combine(destination.FullName, file.Name));
        }

        if (recursively)
        {
            foreach (var directory in self.GetDirectories())
            {
                directory.Copy(destination.CreateSubdirectory(directory.Name), recursively);
            }
        }
    }
}

Example of use:

var sourceDirectory = new DirectoryInfo(@"C:\source");
var destinationDirectory = new DirectoryInfo(@"C:\destination");

if (destinationDirectory.Exists == false)
{
    sourceDirectory.Copy(destinationDirectory, recursively: true);
}
Cyclostome answered 5/1, 2023 at 15:26 Comment(0)
W
0

Better than any code (extension method to DirectoryInfo with recursion)

public static bool CopyTo(this DirectoryInfo source, string destination)
{
    try
    {
        foreach (string dirPath in Directory.GetDirectories(source.FullName))
        {
            var newDirPath = dirPath.Replace(source.FullName, destination);
            Directory.CreateDirectory(newDirPath);
            new DirectoryInfo(dirPath).CopyTo(newDirPath);
        }
        //Copy all the files & Replaces any files with the same name
        foreach (string filePath in Directory.GetFiles(source.FullName))
        {
            File.Copy(filePath, filePath.Replace(source.FullName,destination), true);
        }
        return true;
    }
    catch (IOException exp)
    {
        return false;
    }
}
Wolcott answered 20/10, 2017 at 20:45 Comment(1)
I'm not sure what this adds over the accepted answer, other than using recursion (where that doesn't need to) and hiding exceptions to make debugging harder.Thrips
H
0

Copy and replace all files of the folder

        public static void CopyAndReplaceAll(string SourcePath, string DestinationPath, string backupPath)
    {
            foreach (string dirPath in Directory.GetDirectories(SourcePath, "*", SearchOption.AllDirectories))
            {
                Directory.CreateDirectory($"{DestinationPath}{dirPath.Remove(0, SourcePath.Length)}");
                Directory.CreateDirectory($"{backupPath}{dirPath.Remove(0, SourcePath.Length)}");
            }
            foreach (string newPath in Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories))
            {
                if (!File.Exists($"{ DestinationPath}{newPath.Remove(0, SourcePath.Length)}"))
                    File.Copy(newPath, $"{ DestinationPath}{newPath.Remove(0, SourcePath.Length)}");
                else
                    File.Replace(newPath
                        , $"{ DestinationPath}{newPath.Remove(0, SourcePath.Length)}"
                        , $"{ backupPath}{newPath.Remove(0, SourcePath.Length)}", false);
            }
    }
Heterodyne answered 9/4, 2019 at 15:33 Comment(0)
P
0

The code below is microsoft suggestion how-to-copy-directories and it is shared by dear @iato but it just copies sub directories and files of source folder recursively and doesn't copy the source folder it self (like right click -> copy ).

but there is a tricky way below this answer :

private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs = true)
{
    // Get the subdirectories for the specified directory.
    DirectoryInfo dir = new DirectoryInfo(sourceDirName);

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

    DirectoryInfo[] dirs = dir.GetDirectories();
    // If the destination directory doesn't exist, create it.
    if (!Directory.Exists(destDirName))
    {
        Directory.CreateDirectory(destDirName);
    }

    // Get the files in the directory and copy them to the new location.
    FileInfo[] files = dir.GetFiles();
    foreach (FileInfo file in files)
    {
        string temppath = Path.Combine(destDirName, file.Name);
        file.CopyTo(temppath, false);
    }

    // If copying subdirectories, copy them and their contents to new location.
    if (copySubDirs)
    {
        foreach (DirectoryInfo subdir in dirs)
        {
            string temppath = Path.Combine(destDirName, subdir.Name);
            DirectoryCopy(subdir.FullName, temppath, copySubDirs);
        }
    }
}

if you want to copy contents of source folder and subfolders recursively you can simply use it like this :

string source = @"J:\source\";
string dest= @"J:\destination\";
DirectoryCopy(source, dest);

but if you want to copy the source directory it self (similar that you have right clicked on source folder and clicked copy then in the destination folder you clicked paste) you should use like this :

string source = @"J:\source\";
string dest= @"J:\destination\";
DirectoryCopy(source, Path.Combine(dest, new DirectoryInfo(source).Name));
Paver answered 4/5, 2019 at 9:1 Comment(3)
has been already posted some answers below: https://mcmap.net/q/11185/-copy-the-entire-contents-of-a-directory-in-cNoland
Thanks @MA-Maddin, but does it copy the source folder itself ? or just the contents ?Paver
FYI, VB.NET's new Microsoft.VisualBasic.Devices.Computer().FileSystem.CopyDirectory has override/Ship options and progress bar display... those c# codes are not fully equivalent.Boer
C
0

Below code to copy all files from source to destination of given pattern in same folder structure:

public static void Copy()
{
    string sourceDir = @"C:\test\source\";
    string destination = @"C:\test\destination\";

    string[] textFiles = Directory.GetFiles(sourceDir, "*.txt", SearchOption.AllDirectories);

    foreach (string textFile in textFiles)
    {
        string fileName = textFile.Substring(sourceDir.Length);
        string directoryPath = Path.Combine(destination, Path.GetDirectoryName(fileName));
        if (!Directory.Exists(directoryPath))
            Directory.CreateDirectory(directoryPath);

        File.Copy(textFile, Path.Combine(directoryPath, Path.GetFileName(textFile)), true);
    }
}

enter image description here

Claycomb answered 10/5, 2021 at 18:59 Comment(1)
How is this different from the accepted answer?Thrips
V
0

Just wanted to add my version. It can handle both directories and files, and can overwrite or skip if destination file exists.

public static void Copy(
    string source,
    string destination,
    string pattern = "*",
    bool includeSubFolders = true,
    bool overwrite = true,
    bool overwriteOnlyIfSourceIsNewer = false)
{
    if (File.Exists(source))
    {
        // Source is a file, copy and leave
        CopyFile(source, destination);
        return;
    }

    if (!Directory.Exists(source))
    {
        throw new DirectoryNotFoundException($"Source directory does not exists: `{source}`");
    }

    var files = Directory.GetFiles(
        source,
        pattern,
        includeSubFolders ?
            SearchOption.AllDirectories :
            SearchOption.TopDirectoryOnly);

    foreach (var file in files)
    {
        var newFile = file.Replace(source, destination);
        CopyFile(file, newFile, overwrite, overwriteOnlyIfSourceIsNewer);
    }
}

private static void CopyFile(
    string source,
    string destination,
    bool overwrite = true,
    bool overwriteIfSourceIsNewer = false)
{
    if (!overwrite && File.Exists(destination))
    {
        return;
    }

    if (overwriteIfSourceIsNewer && File.Exists(destination))
    {
        var sourceLastModified = File.GetLastWriteTimeUtc(source);
        var destinationLastModified = File.GetLastWriteTimeUtc(destination);
        if (sourceLastModified <= destinationLastModified)
        {
            return;
        }

        CreateDirectory(destination);
        File.Copy(source, destination, overwrite);
        return;
    }

    CreateDirectory(destination);
    File.Copy(source, destination, overwrite);
}

private static void CreateDirectory(string filePath)
{
    var targetDirectory = Path.GetDirectoryName(filePath);
    if (targetDirectory != null && !Directory.Exists(targetDirectory))
    {
        Directory.CreateDirectory(targetDirectory);
    }
}
Viera answered 31/10, 2021 at 17:58 Comment(0)
O
0

Properties of this code:

  • No parallel task, is less performant, but the idea is to treat file by file, so you can log or stop.
  • Can skip hiddden files
  • Can skip by modified date
  • Can break or not (you chose) on a file copy error
  • Uses Buffer of 64K for SMB and FileShare.ReadWrite to avoid locks
  • Personalize your Exceptions Message
  • For Windows

Notes
ExceptionToString() is a personal extension that tries to get inner exceptions and display stack. Replace it for ex.Message or any other code.
log4net.ILog _log I use ==Log4net== You can make your Log in a different way.

/// <summary>
/// Recursive Directory Copy
/// </summary>
/// <param name="fromPath"></param>
/// <param name="toPath"></param>
/// <param name="continueOnException">on error, continue to copy next file</param>
/// <param name="skipHiddenFiles">To avoid files like thumbs.db</param>
/// <param name="skipByModifiedDate">Does not copy if the destiny file has the same or more recent modified date</param>
/// <remarks>
/// </remarks>
public static void CopyEntireDirectory(string fromPath, string toPath, bool continueOnException = false, bool skipHiddenFiles = true, bool skipByModifiedDate = true)
{
    log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    string nl = Environment.NewLine;

    string sourcePath = "";
    string destPath = "";
    string _exMsg = "";

    void TreateException(Exception ex)
    {
        _log.Warn(_exMsg);
        if (continueOnException == false)
        {
            throw new Exception($"{_exMsg}{nl}----{nl}{ex.ExceptionToString()}");
        }
    }

    try
    {
        foreach (string fileName in Directory.GetFileSystemEntries(fromPath, "*", SearchOption.AllDirectories))
        {
            sourcePath = fileName;
            destPath = Regex.Replace(fileName, "^" + Regex.Escape(fromPath), toPath);

            Directory.CreateDirectory(Path.GetDirectoryName(destPath));
            
            _log.Debug(FileCopyStream(sourcePath, destPath,skipHiddenFiles,skipByModifiedDate));
        }
    }
    // Directory must be less than 148 characters, File must be less than 261 characters
    catch (PathTooLongException)
    {
        throw new Exception($"Both paths must be less than 148 characters:{nl}{sourcePath}{nl}{destPath}");
    }
    // Not enough disk space. Cancel further copies
    catch (IOException ex) when ((ex.HResult & 0xFFFF) == 0x27 || (ex.HResult & 0xFFFF) == 0x70)
    {
        throw new Exception($"Not enough disk space:{nl}'{toPath}'");
    }
    // used by another process
    catch (IOException ex) when ((uint)ex.HResult == 0x80070020)
    {
        _exMsg = $"File is being used by another process:{nl}'{destPath}'{nl}{ex.Message}";
        TreateException(ex);
    }
    catch (UnauthorizedAccessException ex)
    {
        _exMsg = $"Unauthorized Access Exception:{nl}from:'{sourcePath}'{nl}to:{destPath}";
        TreateException(ex);
    }
    catch (Exception ex)
    {
        _exMsg = $"from:'{sourcePath}'{nl}to:{destPath}";
        TreateException(ex);
    }
}

/// <summary>
/// File Copy using Stream 64K and trying to avoid locks with fileshare
/// </summary>
/// <param name="sourcePath"></param>
/// <param name="destPath"></param>
/// <param name="skipHiddenFiles">To avoid files like thumbs.db</param>
/// <param name="skipByModifiedDate">Does not copy if the destiny file has the same or more recent modified date</param>
public static string FileCopyStream(string sourcePath, string destPath, bool skipHiddenFiles = true, bool skipByModifiedDate = true)
{
    // Buffer should be 64K = 65536‬ bytes 
    // Increasing the buffer size beyond 64k will not help in any circunstance,
    // as the underlying SMB protocol does not support buffer lengths beyond 64k."
    byte[] buffer = new byte[65536];

    if (!File.Exists(sourcePath))
        return $"is not a file: '{sourcePath}'";

    FileInfo sourcefileInfo = new FileInfo(sourcePath);
    FileInfo destFileInfo = null;
    if (File.Exists(destPath))
        destFileInfo = new FileInfo(destPath);

    if (skipHiddenFiles)
    {
        if (sourcefileInfo.Attributes.HasFlag(FileAttributes.Hidden))
            return $"Hidden File Not Copied: '{sourcePath}'";
    }

    using (FileStream input = sourcefileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    using (FileStream output = new FileStream(destPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite, buffer.Length))
    {
        if (skipByModifiedDate && destFileInfo != null)
        {
            if (destFileInfo.LastWriteTime < sourcefileInfo.LastWriteTime)
            {
                input.CopyTo(output, buffer.Length);
                destFileInfo.LastWriteTime = sourcefileInfo.LastWriteTime;
                return $"Replaced: '{sourcePath}'";
            }
            else
            {
                return $"NOT replaced (more recent or same file): '{sourcePath}'";
            }
        }
        else
        {
            input.CopyTo(output, buffer.Length);
            destFileInfo = new FileInfo(destPath);
            destFileInfo.LastWriteTime = sourcefileInfo.LastWriteTime;
            return $"New File: '{sourcePath}'";
        }
    }
}
Outdoors answered 23/11, 2021 at 19:8 Comment(0)
U
0

For UWP and Winui 3 (WindowsAppSdk) using Async API:

public async Task CopyAsync(StorageFolder source, StorageFolder dest)
{
    foreach (var item in await source.GetItemsAsync())

        if (item is StorageFile file)
            await file.CopyAsync(dest);

        else if (item is StorageFolder folder)
            await CopyAsync(folder, await dest.CreateFolderAsync(folder.Name, CreationCollisionOption.OpenIfExists));
}
Unquestioned answered 7/9, 2022 at 5:40 Comment(0)
C
0

For completeness, using relative file paths and optionally replacing files:

public static void DuplicateDirectories(
    string sourceDirectory, 
    string targetDirectory,
    string searchPattern = "*.*",
    SearchOption searchOption = SearchOption.AllDirectories)
{
    foreach (string dir in Directory.GetDirectories(sourceDirectory, searchPattern, searchOption)) 
    {
        var relativePath = Path.GetRelativePath(sourceDirectory, dir);
        var targetPath = Path.Combine(targetDirectory, relativePath);
        Directory.CreateDirectory(targetPath);
    }
}

public static void CopyFilesToDirectories(
    string sourceDirectory, 
    string targetDirectory,
    bool replaceIfExists,
    string searchPattern = "*.*",
    SearchOption searchOption = SearchOption.AllDirectories)
{
    foreach (string filePath in Directory.GetFiles(sourceDirectory, searchPattern, searchOption))
    {
        var relativePath = Path.GetRelativePath(sourceDirectory, filePath);
        var targetPath = Path.Combine(targetDirectory, relativePath);
        File.Copy(filePath, targetPath, replaceIfExists);
    }
}

Then:

var sourceDirectory = @"path\to\source";
var targetDirectory = @"path\to\target";

DuplicateDirectories(
    sourceDirectory,
    targetDirectory);

CopyFilesToDirectories(
    sourceDirectory,
    targetDirectory,
    true);
Cist answered 18/3, 2023 at 11:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.