Get list of results from recursive method - c#

I have this code :
private void SearchForDoc()
{
try
{
outputtext = #"c:\temp\outputtxt";
outputphotos = #"c:\temp\outputphotos";
temptxt = #"c:\temp\txtfiles";
tempphotos = #"c:\temp\photosfiles";
if (!Directory.Exists(temptxt))
{
Directory.CreateDirectory(temptxt);
}
if (!Directory.Exists(tempphotos))
{
Directory.CreateDirectory(tempphotos);
}
if (!Directory.Exists(outputtext))
{
Directory.CreateDirectory(outputtext);
}
if (!Directory.Exists(outputphotos))
{
Directory.CreateDirectory(outputphotos);
}
t = Environment.GetEnvironmentVariable(Environment.GetFolderPath(Environment.SpecialFolder.Personal));
ApplyAllFiles(t, ProcessFile);
for (int i = 0; i < textfiles.Length; i++)
{
FileInfo fi = new FileInfo((textfiles[i]));
DirectoryInfo d = new DirectoryInfo(temptxt);
long dirSize = DirSize(d);
if ((dirSize + fi.Length) <= 8388608)
fi.CopyTo(temptxt + "\\" + fi.Name, true);
else
break;
}
Compressions("textfiles.zip", temptxt, outputtext);
The problem is with this part:
for (int i = 0; i < textfiles.Length; i++)
textfiles was string[] but now textfiles is no longer exists since I'm using the ApplyAllFiles method:
static void ProcessFile(string path) {/* ... */}
static void ApplyAllFiles(string folder, Action<string> fileAction)
{
foreach (string file in Directory.GetFiles(folder))
{
fileAction(file);
}
foreach (string subDir in Directory.GetDirectories(folder))
{
try
{
ApplyAllFiles(subDir, fileAction);
}
catch
{
// swallow, log, whatever
}
}
}
EDIT**
In my method now i have:
string t = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string[] textfiles = ApplyAllFiles(t, "*.txt", ProcessFile).ToArray();
Then the ApplyAllFiles method:
static void ProcessFile(string path) {/* ... */}
static IEnumerable<string> ApplyAllFiles(string folder, string searchPattern, Action<string> fileAction)
{
foreach (string file in Directory.GetFiles(folder))
{
fileAction(file);
yield return file;
}
foreach (string subDir in Directory.GetDirectories(folder, searchPattern))
{
// try
//{
foreach (string file in ApplyAllFiles(subDir, searchPattern, fileAction))
{
yield return file;
}
// }
// catch
// {
// swallow, log, whatever
// }
}
}
Had to remove the try in catch yield cant be in try and catch
Second i added searchPatterns variable also in the second inner foreach loop
The result is im getting only pdf or something files not text about 25 files and only from the main dosuments directory.

You can get a list of all files processed by ApplyAllFiles like this:
static IEnumerable<string> ApplyAllFiles(string folder, Action<string> fileAction)
{
IEnumerable<string> files = Directory.GetFiles(folder);
foreach (string file in files)
{
fileAction(file);
}
foreach (string subDir in Directory.GetDirectories(folder))
{
try
{
files = files.Concat(ApplyAllFiles(subDir, fileAction));
}
catch
{
// swallow, log, whatever
}
}
return files;
}
But this will return an IEnumerable<string> to turn it into a string[] use this:
var textfiles = ApplyAllFiles(t, ProcessFile).ToArray();
To only apply the specified action to certain files, you can pass in a new parameter like this:
static IEnumerable<string> ApplyAllFiles(string folder, string searchPattern, Action<string> fileAction)
{
IEnumerable<string> files = Directory.GetFiles(folder, searchPattern);
foreach (string file in files)
{
fileAction(file);
}
foreach (string subDir in Directory.GetDirectories(folder))
{
try
{
files = files.Concat(ApplyAllFiles(subDir, searchPattern, fileAction));
}
catch
{
// swallow, log, whatever
}
}
return files;
}
var textfiles = ApplyAllFiles(t, "*.txt", ProcessFile).ToArray();
Note however, there is already an method that allows you to search a directory recursively. This whole structure can probably be reduced to:
var textfiles = Directory.GetFiles(t, "*.txt", SearchOption.AllDirectories);
foreach(var f in textfiles)
{
ProcessFile(f);
}

Related

Can Not Get Shared Folder Original (System Path)

Get Shared Folder System Path But Code
Return same Path Shared Folder Like '#\123.1.1.56\Pagal'
DirSearch("\\\\DESKTOP-2FFGFDM\\Packages");
static void DirSearch(string dir, string rootDir = null)
{
if (rootDir == null)
{
rootDir = dir;
}
try
{
foreach (string f in Directory.GetFiles(dir))
{
string filename = f.Substring(rootDir.Length);
Console.WriteLine(filename);
}
foreach (string d in Directory.GetDirectories(dir))
{
Console.WriteLine(d);
DirSearch(d, rootDir);
}
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
}
I can Not Find Shared Folder System Path
So Pls. Help me.........
Thanks In Advance
To test if a network share exists you can use a DirectoryInfo
static void DirSearch(string dir, string rootDir = null)
{
if(!new DirectoryInfo(dir).Exists)
throw new Exception($"the provided directory {dir} is not valid.")
if (rootDir == null)
{
rootDir = dir;
}
try
{
foreach (string f in Directory.GetFiles(dir))
{
string filename = f.Substring(rootDir.Length);
Console.WriteLine(filename);
}
foreach (string d in Directory.GetDirectories(dir))
{
Console.WriteLine(d);
DirSearch(d, rootDir);
}
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
}
if this is a school assignment for recursive programming than that would work, however you can just get all files and directory without the recursion
foreach (string f in Directory.GetFiles(dir,"*.*",SearchOption.AllDirectories))
{
string filename = f.Substring(rootDir.Length);
Console.WriteLine(filename);
}
as these files come from directories you can get those out of the name
var dir="";
foreach (string f in Directory.GetFiles(dir,"*.*",SearchOption.AllDirectories))
{
string filename = f.Substring(rootDir.Length);
var directoryName = new FileInfo(filename).Directory.FullName;
if(directoryName!=dir)
{
Console.WriteLine(filename);
dir= directoryName ;
}
Console.WriteLine(filename);
}

Best way to get path all files, subfolder in large directory?

Below is my code to get all files in large directory (Framework 3.5). It run ~ 1 hour, but cannot finish, memory in Task Manager upto 1,600,000K
void ApplyAllFiles(DirectoryInfo dir)
{
String space = " ";
foreach (FileInfo f in dir.GetFiles())
{
try
{
int size = Convert.ToInt32(f.Length / 1024);
if (size > filesize && isFileType(f.FullName) == true)
{
pw.WriteLine(f.LastWriteTime + space + size + space + f.FullName);
}
}
catch
{ }
}
foreach (DirectoryInfo d in dir.GetDirectories())
{
try
{
ApplyAllFiles(d);
}
catch
{
}
}
}
If I use Java code, just 5 min for finish and memory always < 100,000 K. I think C# code maybe has problem (getFiles & getDirectories is not good for large Directory). I hope someone can help.
I SOLVED IT BY TAKE FOREACH LOOP INSIDE TRY { }.
You can create a non-recursive extension method:
public static IEnumerable<FileInfo> GetFilesDepthFirst(this DirectoryInfo root,
string dirPattern = "*", string filePattern = "*.*")
{
var stack = new Stack<DirectoryInfo>();
stack.Push(root);
while (stack.Count > 0)
{
var current = stack.Pop();
IEnumerable<FileInfo> files = Enumerable.Empty<FileInfo>();
IEnumerable<DirectoryInfo> dirs = Enumerable.Empty<DirectoryInfo>();
try
{
#if NET35
dirs = current.GetDirectories(searchPattern: dirPattern);
files = current.GetFiles(searchPattern: filePattern);
#else
dirs = current.EnumerateDirectories(searchPattern: dirPattern);
files = current.EnumerateFiles(searchPattern: filePattern);
#endif
}
catch (UnauthorizedAccessException) { }
catch (PathTooLongException) { }
foreach (FileInfo file in files)
yield return file;
foreach (DirectoryInfo dir in dirs)
stack.Push(dir);
}
}
Which you can call like:
var dInfo = new DirectoryInfo("C:\\");
var allFiles = dInfo.GetFilesDepthFirst(filePattern: "*.txt");
foreach(FileInfo file in allFiles)
{
// build output string
}
As other users have stated, Directory.GetFiles is terrible. Directory.EnumerateFiles is an alternative.
Reference from here

Make C# Search Hard-Drive Exclude Directory

Program goes through directories and prints Avi files to textbox
public FileList()
{
InitializeComponent();
//Sets Drive Choices
DriveInfo[] drives = DriveInfo.GetDrives();
foreach (DriveInfo d in drives)
{
driveChoice.Items.Add(d);
}
}
//Find Video Files
private void btnStart_Click(object sender, EventArgs e)
{
String path = driveChoice.Text;
if (path != "C:\\")
{
String[] allfiles = Directory.GetFiles(path, "*.avi*", System.IO.SearchOption.AllDirectories);
foreach (String file in allfiles)
{
tbFileList.Text = tbFileList.Text + file + "\r\n";
}
}
else
{
Application.Exit();
}
}
}
When ran I get an error.
Unauthorized Access 'I:\$RECYCLE.BIN\S-1-5-21-1332477098-3306142970-3529014387-1000\'
Can I set the program to just skip 'I:\$RECYCLE.BIN'
Looks like you need to switch to a recursive solution or some other loop rather than using 'AllDirectories'. That way you can provide some skip logic.
see this link http://support.microsoft.com/kb/303974
and this code snippet from that page:
void DirSearch(string sDir)
{
try
{
foreach (string d in Directory.GetDirectories(sDir))
{
foreach (string f in Directory.GetFiles(d, txtFile.Text))
{
lstFilesFound.Items.Add(f);
}
DirSearch(d);
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
}
In that code you would just check your sDir for the values you want to skip.
Now there is no way to have the AllDirectories option skip specific directories or ignore exceptions that occur from traversing. You will need to manually search the directory structure and deal with errors that occur
if !filePath.Contains("I:\$RECYCLE.BIN")
Use a lambda statement to exclude the system directories:
public FileList()
{
InitializeComponent();
//Sets Drive Choices
DriveInfo[] drives = DriveInfo.GetDrives();
foreach (DriveInfo d in drives)
{
driveChoice.Items.Add(d);
}
}
//Find Video Files
private void btnStart_Click(object sender, EventArgs e)
{
String path = driveChoice.Text;
if (path != "C:\\")
{
DirectoryInfo root = new DirectoryInfo(path);
var rootFiles = root.GetFiles("*.avi");
var rootDirs = root.GetDirectories("*", SearchOption.TopDirectoryOnly).Where(d => !d.Name.Equals("System Volume Information") && !d.Name.Equals("$RECYCLE.BIN"));
foreach (var file in rootFiles)
{
tbFileList.Text = tbFileList.Text + file.FullName + "\r\n";
}
foreach (var dir in rootDirs)
{
foreach (var dirFile in dir.GetFiles("*.avi", SearchOption.AllDirectories))
{
tbFileList.Text = tbFileList.Text + dirFile.FullName + "\r\n";
}
}
}
else
{
Application.Exit();
}
}
I just tried the usage of the Lambda expression of excluding both folders from the returned string list in VS 2017. I observed something strange. If the lambda expression is directly added to the retrieval of the directories as in the string shown above the list still returns $RECYCLEBIN, however SVI folder is not returned. In order to make the lambda working correctly I needed to separate the 2 actions i.e:
var allDirs = rootDir.GetDirectories("*",SearchOption.TopDirectoryOnly);
var filteredDirs = allDirs.Where(d=> !d.Name.Equals("System Volume Information") && !d.Name.Equals("$RECYCLE.BIN"));

Condensing repetitive if statements

I have the following code, which scans a directory and puts files containing "a" within its filename to a new folder A. Similarly, it puts files with "b" within its filename to a new folder called B. Since the if statements are basically the same, with the only thing that changes being the letter "a" or "b" and being sent to either destA or destb (desitinations), how can I trim this code down? I know there is a better way because much of the code is repeated... Thanks.
static void Main()
{
string path = #"C:\Users\me\Desktop\FOLDER";
string destA = #"C:\Users\me\Desktop\FOLDER\A";
string destB = #"C:\Users\me\Desktop\FOLDER\B";
DirectoryInfo dir = new DirectoryInfo(path);
FileInfo[] filesxx = dir.GetFiles();
foreach (FileInfo filexx in filesxx)
{
if (filexx.Name.Contains("a"))
{
if (!Directory.Exists(destA))
Directory.CreateDirectory(destA);
Console.WriteLine(filexx);
filexx.CopyTo(Path.Combine(destA, filexx.Name), true);
}
else if (filexx.Name.Contains("b"))
{
if (!Directory.Exists(destB))
Directory.CreateDirectory(destB);
Console.WriteLine(filexx);
filexx.CopyTo(Path.Combine(destB, filexx.Name), true);
} 
else
{
Console.WriteLine("Other: ", filexx);
}
}
Console.Read();
}
Create a method like:
private Boolean MoveFile(FileInfo filexx, String nameMatch, String destDirectory) {
Boolean result = false;
if (filexx.Name.Contains(nameMatch)) {
if (!Directory.Exists(destDirectory)) {
Directory.CreateDirectory(destDirectory);
}
Console.WriteLine(filexx);
filexx.CopyTo(Path.Combine(destDirecotry, filexx.Name), true);
result = true;
}
return result;
}
Then just call it as necessary.
foreach(FileInfo filexx in filesxx) {
if (!MoveFile(filexx, "a", destA)) {
if (!MoveFile(filexx, "b", destB)) {
Console.WriteLine("Other: ", filexx);
}
}
}
Of course, there is a potential precedence issue here. What if the file is named "abcd"? Should it go to the A folder or the B folder?
If all you're looking for is less code, this should do it.
public static void Main()
{
const string TargetPath = #"C:\Users\me\Desktop\FOLDER";
var dir = new DirectoryInfo(TargetPath);
var files = dir.GetFiles();
foreach (var file in files.Where(file => !CopyFile(TargetPath, file, "a")).Where(file => !CopyFile(TargetPath, file, "b")))
{
Console.WriteLine("Other: " + file.Name);
}
Console.Read();
}
private static bool CopyFile(string dir, FileInfo file, string match)
{
if (!file.Name.Contains(match))
{
return false;
}
dir = dir + "\\" + match.ToUpper();
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
Console.WriteLine(file);
file.CopyTo(Path.Combine(dir, file.Name), true);
return true;
}

Copy the entire contents of a directory in C#

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?
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);
}
}
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.
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.
}
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.
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.
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)));
}
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;
}
}
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;
}
}
}
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);
}
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));
}
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
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);
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));
}
}
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;
}
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;
}
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);
}
}
}
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);
}
}
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;
}
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);
}
}
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);
}
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;
}
}
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);
}
}
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));
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);
}
}
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);
}
}
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}'";
}
}
}
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));
}
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);
}

Categories

Resources