Copy the entire contents of a directory in C#
Solution 1
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);
}
}
Solution 2
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.
Solution 3
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.
}
Solution 4
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.
Solution 5
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.
Keith
Keith Henry Chief Software Architect, building offline-first and responsive applications in the recruitment industry. I'm also on Linked In. Email me on Google's email, my address is ForenameSurname.
Updated on August 03, 2022Comments
-
Keith over 1 year
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?
-
Eugene Yokota over 15 years
-
Lunatic Experimentalist over 15 yearsI would say that looking at the alternatives posted below, that the VB way doesn't look so ugly.
-
Boris Callens about 15 yearsThe 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.
-
AMissico over 14 yearsHow can it be a hack when it is part of the .NET Framework? Stop writing code and use what you got.
-
Keith over 14 years
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 inSystem.IO
. Also only theSystem.[something]
namespaces are part of Mono. -
AMissico over 14 yearsThat 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.
-
AMissico over 14 yearsIf 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.
-
AMissico over 14 yearsThe 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.
-
AMissico over 14 yearsThere 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.
-
AMissico over 14 yearsThat is a limitation of Mono. Not part of your answer. You want a better way and there is none.
-
AMissico over 14 yearsIf 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.
-
AMissico over 14 yearsYou C# guys just kill me. It is not about tools, it is about solutions.
-
Keith over 14 years@AMissico - ok, so why is this optimised and complete code in
Microsoft.VisualBasic
and notSystem.IO
? The reason it isn't in Mono is because all the libraries that are considered 'core' areSystem.[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 inSystem.IO
. -
RCIX about 14 yearsTo 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 usingSystem.*
libraries, they are potentially constraining themselves from using Mono, which i gather may be a problem from the comments made by the OP. -
jasonh about 14 years@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.
-
jasonh about 14 years@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?
-
Keith about 14 years@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)
-
jasonh about 14 yearsI 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. :)
-
Joe Johnston about 10 yearsI just Q&A'd this with some xcopy like options in C#. stackoverflow.com/questions/22151995/…
-
reggaeguitar over 9 yearsI use robocopy, it works great
-
Ohad Schneider over 3 yearsNote that
Microsoft.VisualBasic.Devices
is not available in .NET core: github.com/dotnet/docs/issues/14546 -
shashwat over 3 yearsUsing this code in production to copy nested directories and files. Didn't find any issue. docs.microsoft.com/en-us/dotnet/standard/io/…
-
juFo over 2 yearsGive this an upvote: github.com/microsoft/dotnet/issues/794
-
Keith over 2 years@juFo, sorry, I really don't think we need it now, I've responded to the GH issue with detail.
-
-
Keith over 15 yearsNowt, 'cept the recursive call. Why do we need to do that in C#?
-
Keith over 15 yearsThat 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.
-
spoulson over 15 yearsThis is head recursion. It can fall prey to a stack overflow if the directories are nested deep enough.
-
Konrad Rudolph over 15 yearsUntil 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.
-
jwmiller5 about 15 yearsIs loading the VB assembly expensive? The VB options are much more elegant than the C# versions.
-
Dmitry Tashkinov over 14 yearsSorry 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.
-
AMissico over 14 yearsWhat "VB's backward-compatibility stuff"? CopyDirectory uses either the Shell or the Framework.
-
AMissico over 14 yearsThis is not a better way. Period. Use the debugged and production ready code that Microsoft provided in the Framework.
-
Konrad Rudolph over 14 years@AMissico: better than what? Nobody claimed it to be better than the VB code from the framework. We know it isn’t.
-
aron about 14 yearswhat do the /E /I stand for? Overwrite?
-
d4nt about 14 years/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.
-
Keith over 13 yearsNeat idea - I don't know why I never thought of using
SearchOption.AllDirectories
. I'd probably use theSubString
method rather thanReplace
, but that's just coding style stuff. -
jaysonragasa almost 13 yearsadd double quote to be safe.
-
Alex over 12 yearsIt'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.
-
Gaurav Arora about 12 yearsAwesome! Its resolve my issue. But is there any way to copy/move files with billion of numbers and without removing existing files/folders ?
-
Jon Crowell about 12 yearsAdd /Y to prevent getting prompted to overwrite existing files. stackoverflow.com/q/191209/138938
-
joerage almost 12 yearsBe 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.
-
Keith over 11 yearsSee 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. -
Vishal_Kotecha over 11 years@Alex - What makes String.Substring better than String.Replace in this case?
-
Keith over 11 years@Alex - that's exactly why I'd use the
SubString
method rather thanReplace
-
Keith over 11 years@Xaisoft -
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"
-
Keith over 11 years@Rick this method does
File.Copy
so it leaves the original. If you wanted to remove the original you could useFile.Move
or (probably better) add a loop to delete the files after the copy is complete. -
smirkingman over 11 yearsAdding the /d option is useful to only copy modified files, along with the /i option
-
Daryl about 11 yearsWhy
*.*
instead of*
? Don't you want to copy files without extensions too? -
Dave over 10 yearsShould 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!
-
Pete Kirkham almost 10 years@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. -
wlyles over 9 yearsI 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) -
Piotr Owsiak over 9 years@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?
-
django11 over 9 yearsSeems 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/…
-
Ohad Schneider over 9 yearsIf 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 /E
-
toddmo about 9 yearsUpvoted, and added an answer which is a variation of this, where the
source
itself becomes a folder undertarget
, then it's children go under that. -
Dawid Ohia almost 9 yearsNice answer, but one remark: Isn't newPath variable name a little bit misleading? Shouldn't it be called rather sourcePath or sourceFilePath?
-
Adnan Ali over 8 yearsWhat if we only want to files from directory and sub directories. and dont want directories? Any one ?
-
Josh M. over 8 yearsI do wish it was on
System.IO.Directory
, but it's better than rewriting it! -
Tal Jerome over 8 yearsThere's no reason to check if the directory exists, simply call Directoty.CreateDirectory which will do nothing if the directory already exists.
-
Alexey F over 8 yearsRemember about the trailing backslash
-
John Smith over 8 years@Keith the chances of that are small, but i still agree that substring is a more correct way then to call replace
-
A.K almost 8 yearsFor those looking to deal with paths greater than 256 characters, you can use a Nuget package called ZetaLongPaths
-
Lzh almost 8 yearsLet's build something and contribute it to the Open Source .NET Core... :/
-
reggaeguitar almost 8 yearsThis is the way to go imo, much easier than any of the other options
-
cel sharp over 7 yearsSorry, 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.
-
KMX over 7 years@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.
-
Gokhan Kurt about 7 yearsThis fails when the directory contains any junction link or symbolic link. Is there any way to overcome that?
-
Keith over 6 yearsThis 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 usesSearchOption.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 toCopyTo()
so that it becamesourceDir.CopyTo(destination);
-
Andy over 6 yearsFolks, use
Path.Combine()
. Never use string concatenation to put file paths together. -
Andy over 6 yearsFolks, use
Path.Combine()
. Never use string concatenation to put file paths together. -
Andy over 6 yearsThis 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. -
Keith over 6 yearsI'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.
-
Matt over 6 yearsIt 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.
-
PellucidWombat about 6 yearsYou have an OBOB in the above code snippet. You should be using
source_dir.Length + 1
, notsource_dir.Length
. -
Minh Nguyen about 6 yearsuseful non-recursion template :)
-
Jean Libera almost 6 yearsThis 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))
-
ewwink almost 6 yearswhat are better or faster,
xcopy.exe
orSystem.IO
? -
eduardomozart almost 6 yearsThank you @JeanLibera, you're right. I changed the code with your suggestion.
-
juFo almost 6 yearsThis is the way! Maybe we should create a UserVoice or github request to add this code to system.io.directory !
-
dgzornoza almost 6 yearsreplace 'file_name.Substring(..)' with 'Path.GetFileName(file_name)'
-
eduardomozart almost 6 yearsI believe that the replacement will not work @dgzornoza 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.
-
dgzornoza almost 6 yearsyes, is not valid GetFileName. Best solution is Substring, replace from previous answer is not safe. Thanks
-
Keith almost 6 yearsNeither 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.
-
AlexanderD almost 6 yearsThe 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 case
-
Keith almost 6 yearsYeah, 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.
-
AlexanderD almost 6 yearsI switched to the answer provided by @justin-r. Still, I'll leave this answer there as just another way of doing it
-
Ed S. almost 6 yearsHard to imagine blowing the stack before glowing the path limit
-
juanora over 5 yearsBest answer, but you should use
source_dir.Length + 1
as mentioned orTrimStart('\\'))
-
Danny Parker over 5 yearsIf the folders are on separate network shares and contain a lot of files, this would be the best option in my opinion.
-
DaedalusAlpha over 5 yearsThis answer seems to be the most useful of them all. By using DirectoryInfo instead of strings a lot of potential problems are avoided.
-
jrh about 5 yearsNote that Microsoft uses SHFileOperation internally for Microsoft.VisualBasic.
-
Keith about 5 yearsCheers for the answer, but I'm not sure what this adds. Also the
try
catch
throw
is pointless. -
Martin Schneider almost 5 yearshas been already posted some answers below: stackoverflow.com/a/45199038/1951524
-
Arash.Zandi almost 5 yearsThanks @MA-Maddin, but does it copy the source folder itself ? or just the contents ?
-
jimbobmcgee over 4 yearsIf you're going to use
Regex
, you should probably alsoRegex.Escape(path)
as part of your expression composition (especially considering the Windows path separator). You might also get benefit from creating (and maybe compiling) anew Regex()
object outside of the loop, rather than relying on the static method. -
Phathutshedzo Khabubu over 4 yearsThe
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 thrown -
The Lemon almost 4 yearsI'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 solution
-
Konrad Rudolph almost 4 years@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. -
Ohad Schneider over 3 yearsVery 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). -
Venugopal M over 3 yearsNice. 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 !!!
-
davrob01 over 3 yearsIn my case, for the directories, I had to use
Path.Join()
instead ofPath.Combine()
. I don't fully understand why though, but I guess I was doing something related to this remark in the documentation, which recommendsPath.Join()
-
Keith almost 3 yearsHow is this different from the accepted answer?
-
Jonney almost 3 yearsFYI, 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.
-
djrecipe almost 3 yearsThis doesn't work for me on Linux. Just use the recursive example outlined by Microsoft at https://docs.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories
-
Youkko over 2 yearsIt'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). -
Youkko over 2 yearsIn 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...