Check file exists and has the right naming standard in C# - c#

We have a process from third-party vendor to drop sales and invetory data everyday and could have any of the following scenarios
Drop the right file. (Naming standard: test.xls)
Drop the right file but not follow the right naming standard. (Other
names could be test_mmddyyyy or testmmddyyyy)
No file dropped.
I am trying to build my logic around these scenarios and stuck at how to build my logic when the file exists but does not have the right naming standard and check for this condition and change the name of the file to the appropriate naming standard.
public void Main()
{
try
{
string filefullpathname = #"C:\Temp\test.xls";
if (File.Exists(filefullpathname) == false)
{
Console.WriteLine("File does not exist in the path");
}
// file exists but right naming standard not followed (Other names could be test_mmddyyyy or testmmddyyyy)
// how to check for this condition and change the name of the file to the naming standard
else
{
string dirname = #"C:\Temp\";
DirectoryInfo directory = new DirectoryInfo(dirname);
string filepartialname = "test";
FileInfo[] fileindirectory = directory.GetFiles(filepartialname + "*");
foreach (FileInfo filename in fileindirectory)
{
string fullname = filename.FullName;
bool ind = Path.HasExtension(fullname);
if (ind == false)
{
File.Move(fullname, directory + filepartialname + ".xls");
}
else
{
File.Move(fullname, directory + filepartialname + ".xls");
}
}
}
Dts.TaskResult = (int)ScriptResults.Success;
}
catch (Exception error)
{
Console.WriteLine(error);
}
}

It is not really clear as to if it is only the file name or a missing extension. So I put in both.
public void Main()
{
try
{
string dirname = #"C:\Temp\";
DirectoryInfo directory = new DirectoryInfo(dirname);
string filepartialname = "test";
FileInfo[] fileindirectory = directory.GetFiles(filepartialname + "*");
foreach (FileInfo filename in fileindirectory)
{
if (filename.Extension == "")
{
//doesn't have an extension
}
else if (!Regex.IsMatch(filename.Name.Replace(filename.Extension, ""), #"^[A-Z|a-z]$"))
{
//contains more then just test
}
else
{
//file is good
}
}
}
catch (Exception error)
{
Console.WriteLine(error);
}
}

Your explanation of what your inputs could be, and how you want to move those inputs isn't super clear, but this should get you started:
var expectedFilename = Path.Combine(someOtherDirectory, "test.xls");
// Matches test* and *.xls
var relevantFiles = Directory
.EnumerateFiles(searchDirectory, "*", SearchOption.TopDirectoryOnly)
.Where(f => Path.GetFileName(f).StartsWith("test") || Path.GetExtension(f).Equals(".xls"))
foreach (var file in relevantFiles)
{
// If there's more than one file matching the pattern, last one in wins
File.Move(file, expectedFilename);
}

Related

(Updated) Working with files, check if exists or not

I am working with files on C# and I got to a point where I don't know how to continue anymore.
The scenario is this: If I upload 3 or more files with the same name at the same time, I want to handle them and change their name to from "myfile.pdf" to "myfile.pdf(1)/(2)/(3)..." depending on how much files I upload.
This is what I have tried so far and in this case, this only works for only the second file because when the third one comes, it will check there is any file with the same - yes, okay name it "myfile.pdf(2) - but this exists too so it will go to another place.
How can I achieve having the same three files in the same folder with this naming convention?
Here's what I have tried so far:
string FileName = "MyFile.pdf";
string path = #"C:\Project\MyPdfFiles\"
if (File.Exists(path))
{
int i = 1;
var FileExists = false;
while (FileExists==false)
{
if (FileExists == false)
{
FileName = FileName + "(" + i + ")";
}
else
return;
i++;
}
}
And the result of this code is: "MyFile.pdf", "MyFile.pdf(1)" And the third one doesn't load here.
I think I'm missing something in the loop or idk :(.
Can someone help me?
I have tried also this:
if(File.Exists(path) || File.Exists(path+"(")
//because when the second file its uploaded, its name will be SecondFile.pdf(1), so this will return true and will proceed running, but still the iteration will "always" start from 0 since everytime I upload a file, I have to refresh the process.
Don't use return inside your while loop, better set 'FileExists = true' whenever you want you loop to stop. A return statement will exit your current method.
I think your problem can be easily solved using recursion, something like this (untested):
public class Program
{
public string FileName { get; set; }
public Program() {
string fileName = "MyFile.pdf";
string path = #"C:\Project\MyPdfFiles\";
FileName = CheckFileName(path, fileName);
}
public string CheckFileName(string path, string fileName, int iteration = 0) {
if (File.Exists($"{path}{fileName}")) {
iteration++;
CheckFileName(path, $"{fileName}({iteration})", iteration);
}
return fileName;
}
}
What this does is: it CheckFileName method will keep calling itself until it finds a name that doesn't exist yet.
This should do the job.
public class Program
{
public static string GetUnusedFilePath(string directorypath, string filename, string ext)
{
string fullPath = $"{directorypath}{filename}{ext}";
int inc = 0;
// check until you have a filepath that doesn't exist
while (File.Exists(fullPath))
{
fullPath = $"{directorypath}{filename}{inc}{ext}";
inc++;
}
return fullPath;
}
public static void UploadFile(string filepath)
{
using (FileStream fs = File.Create(filepath))
{
// Add some text to file
Byte[] title = new UTF8Encoding(true).GetBytes("New Text File");
fs.Write(title, 0, title.Length);
}
}
public static void Main()
{
string[] filestoUpload = { "file", "file", "file", "anotherfile", "anotherfile", "anotherfile" };
string directorypath = #"D:\temp\";
string ext = ".txt";
foreach(var file in filestoUpload)
{
var filePath = GetUnusedFilePath(directorypath, file, ext);
UploadFile(filePath);
}
}
}
I solved this by creating new folders with special names using the code below:
DirectoryInfo hdDirectoryInWhichToSearch = new DirectoryInfo(FileDirectory);
FileSystemInfo[] filesAndDirs = hdDirectoryInWhichToSearch.GetFileSystemInfos("*" + FullFileName + "*");
int i = filesAndDirs.Length;
if (i>1)
{
FileName = Filename + "(" + i ")";
}
So what this does is that it will count how many files we have in that folder with the same name, so I have to check if we have more than 1 file, then change it's name to file(1).
Thank you to everyone that tried to help me, much appreciated.

how to alert the user on duplications in the moving file procces in a Web-Application

I have a source and destination path with the same folder and file names (source has some extra files). my question is when I have cut source locations files and folders and to paste the destination location
how to copied initially the extra files(destination not having files)?
how to through the error after paste the extra files "the folder and files already exist do you want to replace it" message?
after getting the response how can I move and delete the source files?
somebody can help me guys I am stuck with this logic nearly 2 days.
Note: am the beginner of the C# server side code.
thanks, advance. Hi All, thank you for your reply, I have written the same structure with #RezaNoei mentioned my code was
private void DirectoryCopy(string sourceDirName, string destDirName, bool replace, string action)
{
try
{
// Gets the subdirectories for the specified directory.
var dir = new DirectoryInfo(sourceDirName);
var dirs = dir.GetDirectories();
// If the destination directory doesn't exist, creates it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
// Gets the files in the directory and copy them to the new location.
var files = dir.GetFiles();
foreach (var file in files)
{
var oldPath = Path.Combine(sourceDirName, file.Name);
var temppath = Path.Combine(destDirName, file.Name);
var fileExist = File.Exists(temppath);
if (!fileExist)
{
if (action != "paste")
{
file.CopyTo(temppath, true);
}
else
{
File.Move(oldPath, temppath);
}
}
else if (fileExist && replace)
{
File.Delete(temppath);
if (action != "paste")
{
file.CopyTo(temppath, true);
}
else
{
File.Move(oldPath, temppath);
}
}
}
if (action == "paste")
{
DeleteDirectory(sourceDirName);
}
}
catch (Exception e)
{
throw e;
}
}
Use this Function:
Note: If you are developing a web-application and you want to alert this through Html, it doesn't help you. Please read the Next Section "For Web-Application"
Note2: this function doesn't check for inner folders. so if you have a nested path, we should write a recursive function.
public void Copy(string Source, string Destination)
{
string[] SourceFiles = System.IO.Directory.GetFiles(Source);
for (int i = 0; i < SourceFiles.Length; i++)
{
string DestinationFilePath = System.IO.Path.Combine(Destination, System.IO.Path.GetFileName(SourceFiles[i]));
if (System.IO.File.Exists(DestinationFilePath))
{
var DialogResult = MessageBox.Show($"File `{System.IO.Path.GetFileName(SourceFiles[i])}` Exists in the Destination. Are you want to overwrite this file ?", "File Exist !", MessageBoxButtons.YesNo);
if (DialogResult == DialogResult.Yes)
System.IO.File.Copy(SourceFiles[i], DestinationFilePath, true);
}
else
{
System.IO.File.Copy(SourceFiles[i], DestinationFilePath);
}
}
}
For Web-Application:
You actually need to interact with User. And it make our work more complicated than before.
You will Work with Session and you must keep track of this Operation. I don't know which technology are you using but you must but added these ones to Session:
1-1 **Destination Folder**
1-2 **List of Duplicated SourceFiles**
So:
Next Step is to copy non-duplicated files and Added Duplication on Session:
public void Copy(string Source, string Destination)
{
/// Set Session ....
Session["Destination"] = Destination;
List<string> DuplicatedFiles = new List<string>();
string[] SourceFiles = System.IO.Directory.GetFiles(Source);
for (int i = 0; i < SourceFiles.Length; i++)
{
string DestinationFilePath = System.IO.Path.Combine(Destination, System.IO.Path.GetFileName(SourceFiles[i]));
if (System.IO.File.Exists(DestinationFilePath))
{
// Add into Duplication List
DuplicatedFiles.Add(SourceFiles[i]);
}
else
{
System.IO.File.Copy(SourceFiles[i], DestinationFilePath);
}
}
/// Set Session .....
Session["DouplicatedFiles"] = DuplicatedFiles;
}
above code is psudo and the goal is Clear.
Next Step is to show the result of copying or duplications:
I don'n know how do you want to implement such a view for duplication errors and it will not the part of the answer. anyway you may want to let the user to choose the action on each files separately or whole of them at the same time.
depend on your preferences you will have an ActionMethod (In MVC) or something else in WebForm that will do these things:
(If user doesn't want replace the files, it's easy to forget the action)
public void CopyDuplications(bool Overwrite)
{
if (!Overwrite)
return "OK";
else
{
string Destination = Session["Destination"] as string;
var DuplicatedFiles = Session["DouplicatedFiles"] as List<string>();
for (int i = 0; i< DuplicatedFiles.Count; i++)
{
string DestinationFilePath = System.IO.Path.Combine(Destination, System.IO.Path.GetFileName(DuplicatedFiles[i]));
System.IO.File.Copy(DuplicatedFiles[i], DestinationFilePath, true);
}
}
}

C# structure for If.. And If... And If

I am using Visual Studio 2013 Express and I am new to Visual C#. I am sure there is a better way to do what I am trying and I would appreciate any suggestions.
The code I'm trying to write evaluates a series of tests and sets a flag only if all tests = TRUE. I'm currently using six nested if structures to get this done and, while it works, I'm looking for a cleaner, more professional solution. Here's the sample (shortened to three levels for this post):
private const string sDrive = "D:\\";
private const string sFolder = "FTP\\";
private const string sDivFolder = "ABC";
private static bool bSanity = false;
private static void SanityCheck()
{
if (Directory.Exists(sDrive))
{
if (Directory.Exists(sDrive + sFolder))
{
if (Directory.Exists(sDrive + sFolder + sDivFolder))
{
bSanity = true;
}
else
{
Console.WriteLine("FATAL: Div folder doesn't exist.");
}
}
else
{
Console.WriteLine("FATAL: Root folder doesn't exist.");
}
}
else
{
Console.WriteLine("FATAL: Disk drive doesn't exist.");
}
}
The main issue is whether you need to keep the same error reporting you have now. In general, if that is required, I think this is simpler to handle by inverting the cases. That will allow you to remove the nesting by using if/else if:
private static void SanityCheck()
{
if (!Directory.Exists(sDrive))
{
Console.WriteLine("FATAL: Disk drive doesn't exist.");
}
else if (!Directory.Exists(Path.Combine(sDrive, sFolder))
{
Console.WriteLine("FATAL: Root folder doesn't exist.");
}
else if (!Directory.Exists(Path.Combine(sDrive, sFolder, sDivFolder))
{
Console.WriteLine("FATAL: Div folder doesn't exist.");
}
else
{
bSanity = true;
}
}
If the detailed error reporting is not required, you can just check for the lowest level folder directly:
private static void SanityCheck()
{
if (Directory.Exists(Path.Combine(sDrive, sFolder, sDivFolder))
bSanity = true;
else
Console.WriteLine("FATAL: Drive or folder doesn't exist.");
}
if (Directory.Exists(sDrive) && Directory.Exists(sDrive + sFolder) && Directory.Exists(sDrive + sFolder + sDivFolder))
{
bSanity = true;
}
else
{
Console.WriteLine("FATAL: Disk drive doesn't exist.");
}
&& is an early exit operator.
How about using a loop and an array?
// set path array
var paths = new[] { sDrive, sFolder, sDivFolder };
// use StringBuilder for faster string concatenation
var sb = new StringBuilder();
foreach (var p in paths)
{
// append next part of the path
sb.Append(p);
// check if it exists
if (!Directory.Exists(sb.ToString()))
{
// print info message and return from method, because path is incorrect
Console.WriteLine("FATAL: \"{0}\" path doesn't exist.", sb.ToString());
return;
}
}
// we are here, so the whole path works and we can set bSanity to true
bSanity = true;
You can easily manipulate how deap the check is by changing array length. And it will print you exactly what part of the path is not correct.
So one possible cleaner solution might be this:
private static List<Tuple<string, string>> _dir = new List<Tuple<string, string>>
{
Tuple.Create(#"D:\", "FATAL: Disk drive doesn't exist."),
Tuple.Create("FTP", "FATAL: Root folder doesn't exist."),
Tuple.Create("ABC", "FATAL: Div folder doesn't exist."),
}
private static void SanityCheck()
{
var path = string.Empty;
foreach (var t in _dir)
{
path = Path.Combine(path, t.Item1);
if (!Directory.Exists(path))
{
Console.WriteLine(t.Item2);
break;
}
}
}
This isn't exactly the same behaviour as the original, but it is much simpler.
if (Directory.Exists(Path.Combine(sDrive, sFolder, sDivFolder))
bSanity = true;
else
Console.WriteLine("FATAL: Div folder doesn't exist.");

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