Excluding text files etc. from a backup application C# - c#

private void btn_Backup_Click(object sender, EventArgs e)
{
List<DirectoryInfo> SourceDir = this.lbox_Sources.Items.Cast<DirectoryInfo>().ToList();
List<DirectoryInfo> TargetDir = this.lbox_Targets.Items.Cast<DirectoryInfo>().ToList();
foreach (DirectoryInfo sourcedir in SourceDir)
{
foreach (DirectoryInfo targetdir in TargetDir)
{
string dateString = DateTime.Now.ToString("MM-dd-yyyy_H.mm.ss");
string LogFileName = #"BackupLog_" + sourcedir.Name + #"_" + dateString + #".log";
string[] lines = { dateString + "\t" + sourcedir.FullName + "\t" + targetdir.FullName + "\t" + "COMPLETED" };
if (this.checkbox_zipfiles.Checked == true)
{
System.IO.Compression.ZipFile.CreateFromDirectory(sourcedir.FullName, targetdir.FullName + #"\BACKUP_" + sourcedir.Name + #"_" + dateString + #".zip");
System.IO.File.WriteAllLines(tbox_LogFiles.Text + #"\" + LogFileName, lines);
}
else
{
foreach (var file in sourcedir.GetFiles())
{
Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(sourcedir.FullName, targetdir.FullName, true);
System.IO.File.WriteAllLines(tbox_LogFiles.Text + #"\" + LogFileName, lines);
}
}
}
}
}
I need to exclude certain files from the backup (like .txt .docx)
I am using a list on my Form to add those exceptions.
I will also need to exclude certain Files and Folders but I think I can do that if I know how to do this.
private void btn_AddFileTypeException_Click(object sender, EventArgs e)
{
Form_FileTypeExceptions frm = new Form_FileTypeExceptions(new FileException());
if (frm.ShowDialog() == DialogResult.OK)
{
this.lbox_FileTypeExceptions.Items.Add(frm.Exception);
}
}
Any ideas please?

From form where you're setting extensions to be excluded fill list of strings that will contain extensions to skip, something like this:
List<string> extensionsToSkip = new List<string>();
extensionsToSkip.Add(".txt");
extensionsToSkip.Add(".docx");
//etc...
in your inner loop, change foreach loop from
foreach (var file in sourcedir.GetFiles())
into this
foreach (var file in sourcedir.GetFiles()
.Where(f => !extensionsToSkip.Contains(f.Extension)).ToList())
as you can see, when you get file collection with GetFiles it will be filtered to exclude extensions specified in extensionsToSkip list.
before that mentioned loop, test if you're getting right number of files by observing there two lists (just for test):
var originalList = sourcedir.GetFiles();
var filteredList = sourcedir.GetFiles().Where(f => !extensionsToSkip.Contains(f.Extension)).ToList();

Related

Find matching files in a folder (minus file extension) and move them to a new folder

I have a directory of files that contain both Word and PDF files. Some of the Word files in the directory have the same filename (minus extension) as the PDF files in the same directory. I have setup a simple C# winforms application to loop through the files and move the Word documents that have same name as PDF documents. Here's what I have so far. I'm not sure why this isn't working:
string[] filesWORD = Directory.GetFiles(#"c:\test\", "*.docx");
List<string> resultFiles = new List<string>();
foreach (var file in filesWORD)
{
var finalfile = file.Substring(0, file.LastIndexOf(".")); // removes everything after period in name.
resultFiles.Add(finalfile);
listBox1.DataSource = resultFiles.Distinct().ToList(); // placing the Word files in listBox1
}
string[] filesPDF = Directory.GetFiles(#"c:\test\", "*.pdf");
List<string> resultFilesPDF = new List<string>();
foreach (var file in filesPDF)
{
var finalfile = file.Substring(0, file.LastIndexOf("."));
resultFilesPDF.Add(finalfile);
listBox2.DataSource = resultFilesPDF.Distinct().ToList(); // placing the PDF files in listBox2
}
for (int i = 0; i < listBox1.Items.Count; i++)
{
//IF the WORD files in listBox1 match the PDF files in listBox2 -- move them to a new folder.
foreach (string files in listBox1.Items)
{
if (listBox1.Items == listBox2.Items)
{
//Get Filename
var filename = Path.GetFileName(files + ".docx");
//Move Files
File.Move(files + ".docx", #"c:\test2\" + "\\" + filename);
}
}
}
The final for loop is where the problem is, you can try this (need to add listbox for your case), you are comparing the wrong thing, also the outer for loop is not required.
foreach (var pdfFile in resultFilesPDF)
{
foreach (var wordFile in resultFiles)
{
if (wordFile == pdfFile)
{
//Get Filename
var filename = System.IO.Path.GetFileName(wordFile + ".docx");
//Move Files
File.Move(wordFile + ".docx", #"c:\test2\" + "\\" + filename);
}
}
}
Using Linq you can do it like, be aware that if you try to move the same file multiple times the Move method might blow up.
var sameNames = resultFiles.SelectMany(w => resultFilesPDF.Where(p => p == w));
sameNames.ToList().ForEach(file =>
{
File.Move(file + ".docx", #"c:\test2\" + "\\" + System.IO.Path.GetFileName(file + ".docx"));
});

C# access to TreeNode variable file.CreationTime outside treeView

I need to access to file.CreationTime of selected node in treeView and display it in label outside treeView.
I added .Tag and now in works great when directory is selected, but when file is selected I'm getting that treeView1.SelectedNode.Tag is null and application crashes.
Does anyone have idea how to fix it?
private void ListDirectory(TreeView treeView, string path)
{
treeView1.Nodes.Clear();
var rootDirectoryInfo = new DirectoryInfo(path);
treeView1.Nodes.Add(CreateDirectoryNode(rootDirectoryInfo));
}
private static TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeNode(directoryInfo.Name + " (" + DirectorySize(new DirectoryInfo(directoryInfo.FullName)) + " bytes)" + " (" + directoryInfo.GetFileSystemInfos().Length + " files)");
foreach (var directory in directoryInfo.GetDirectories())
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
foreach (var file in directoryInfo.GetFiles())
directoryNode.Nodes.Add(new TreeNode(file.Name + " (" + file.Length + " bytes)"+ file.CreationTime));
directoryNode.Tag = directoryInfo;
return directoryNode;
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
if (treeView1.SelectedNode.Tag!=null)
{
var directoryInfo = treeView1.SelectedNode.Tag as DirectoryInfo;
var creationTime = directoryInfo.CreationTime.ToString();
label1.Text = creationTime;
var lastAccessTime = directoryInfo.LastAccessTime;
label2.Text = lastAccessTime.ToString();
var lastWriteTime = directoryInfo.LastWriteTime;
label3.Text = lastWriteTime.ToString();
}
else
{
label1.Text = "";
label2.Text = "";
label3.Text = "";
}
}
You are not adding DirectoryInfo after iterating through the files.
private static TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeNode(directoryInfo.Name /*+ " (" + DirectorySize(new DirectoryInfo(directoryInfo.FullName)) + " bytes)" + " (" + directoryInfo.GetFileSystemInfos().Length + " files)"*/);
foreach (var directory in directoryInfo.GetDirectories())
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
foreach (var file in directoryInfo.GetFiles())
directoryNode.Nodes.Add(new TreeNode(file.Name + " (" + file.Length + " bytes)" + file.CreationTime));
directoryNode.Tag = directoryInfo;
return directoryNode;
}
Also, you may use FileSystemInfo for Tag, instead of just DirectoryInfo. DirectoryInfo works fine for Directories and Files, however it makes it easy to access the Tag in treeView1_AfterSelected consistently for both Directories and Files if it is FileSystemInfo.
Note that DirectoryInfo and FileInfo are inherited from FileSytemInfo.
You need to add FileInfo to the tree node. Then cast both DirectoryInfo and FileInfo to FileSystemInfo to get creation time, etc.
private static TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
//original code...
// Note: When creating tree node for displaying file,
// assign FileInfo to fileNode.Tag
foreach (var file in directoryInfo.GetFiles()) {
var fileNode = new TreeNode(file.Name + " (" + file.Length + " bytes)"+ file.CreationTime);
fileNode.Tag = file;
directoryNode.Nodes.Add(fileNode);
}
//original code...
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
// Note: Both DirectoryInfo and FileInfo, inherrits FileSystemInfo
// thus you can cast both to FileSystemInfo.
var fsInfo = treeView1.SelectedNode.Tag as FileSystemInfo;
if (fsInfo != null)
{
var creationTime = fsInfo.CreationTime.ToString();
label1.Text = creationTime;
var lastAccessTime = fsInfo.LastAccessTime;
label2.Text = lastAccessTime.ToString();
var lastWriteTime = fsInfo.LastWriteTime;
label3.Text = lastWriteTime.ToString();
}
else
{
label1.Text = "";
label2.Text = "";
label3.Text = "";
}
}
Because you don't add any tag to the TreeNode created when you loop over the FileInfo array returned by DirectoryInfo.GetFiles()
private static TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeNode(directoryInfo.Name + " (" +
DirectorySize(directoryInfo) +
" bytes)" + " (" + directoryInfo.GetFileSystemInfos().Length +
" files)");
foreach (var directory in directoryInfo.GetDirectories())
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
foreach (var file in directoryInfo.GetFiles())
{
TreeNode node = new TreeNode(file.Name + " (" + file.Length + " bytes)"+ file.CreationTime);
directoryNode.Nodes.Add(node);
node.Tag = file;
}
directoryNode.Tag = directoryInfo;
return directoryNode;
}

Check if a file name exists more than once in a FileInfo array

FileInfo[] folderFiles = folder.GetFiles();
foreach (FileInfo file in folderFiles)
{
int fileCount = 0;
StreamWriter sw = null;
string fileName = Path.GetFileNameWithoutExtension(file.Name);
string[] brkedfilename = fileName.Split('_');
string stringToCheck = brkedfilename[3];
for (int i = 0; i <= folderFiles.Count() - 1; i++)
{
string fileName2 = folderFiles[i].Name;
string[] brkedfilename2 = fileName2.Split('_');
if (brkedfilename2[3] == stringToCheck)
{
fileCount = ++fileCount;
if (fileCount == 2)
{
sw = new StreamWriter(folderPath + "/" + newFileName, true);
sw.WriteLine(stringToCheck + " " + "--" + " " + "Repeated in folder " + " " + folder.Name);
sw.Close();
}
}
}
}
By doing this way, if a file name is existing 2 times , it is writing that file name 2 times. But I want it to write only once.
Any help would be appreciated. Thanks in advance. :)
You can use LINQ to find files with same brkedNames:
var repeatedFiles = from f in folderFiles
let brkedName = Path.GetFileNameWithoutExtension(f.Name).Split('_')[3]
group f by brkedName into g
where g.Count() > 1
select new
{
BrkedName = g.Key,
Count = g.Count(),
Files = g
};
This query groups all files by 4th part of splitted names (note: as in your original code I don't check if there is at least 4 parts) and then select groups with more than one file. It also provides some stats - brked name, count of files with same name and files collection.
foreach(var group in repeatedFiles)
sw.WriteLine($"{group.BrkedName} -- repeated {group.Count} times in {folder.Name}");
You can use Dictionary ( filename, repeat time) to list all filename exist. And after that you can loop the dictionary to write to console.
I propose use a hashset. Hashset cannot contain duplicate elements. When You add duplicate element do hashset method Add return false and program go on witout error but hashset will not take duplicate element
var duplicate = new HashSet<string>();
foreach (FileInfo file in folderFiles)
{
int fileCount = 0;
StreamWriter sw = null;
string fileName = Path.GetFileNameWithoutExtension(file.Name);
string[] brkedfilename = fileName.Split('_');
string stringToCheck = brkedfilename[3];
if (!duplicate.Add(stringToCheck))
{
sw = new StreamWriter(folderPath + "/" + newFileName, true);
sw.WriteLine(stringToCheck + " " + "--" + " " + "Repeated in folder " + " " + folder.Name);
sw.Close();
}
}
Program adds each element do Hashset duplicate when element is in Hashset already Add metod return false when I know the element is in the Hashset and I save in the file as You want

Recursive Directory Structure Listing Taking To Long To Process

I am using the code below start at a path (root) provided by a GET variable and recursively go into every sub folder and display it's contents as list items. The path I'm using has about 3800 files and 375 sub folders. I takes about 45 seconds to render the page, is there any way I can cut this time down as this is unacceptable for my users.
string output;
protected void Page_Load(object sender, EventArgs e) {
getDirectoryTree(Request.QueryString["path"]);
itemWrapper.InnerHtml = output;
}
private void getDirectoryTree(string dirPath) {
try {
System.IO.DirectoryInfo rootDirectory = new System.IO.DirectoryInfo(dirPath);
foreach (System.IO.DirectoryInfo subDirectory in rootDirectory.GetDirectories()) {
output = output + "<ul><li><a>" + Regex.Replace(subDirectory.Name, "_", " ");
if (subDirectory.GetFiles().Length != 0 || subDirectory.GetDirectories().Length != 0) {
output = output + " +</a>";
} else {
output = output + "</a>";
}
getDirectoryTree(subDirectory.FullName);
if (subDirectory.GetFiles().Length != 0) {
output = output + "<ul>";
foreach (System.IO.FileInfo file in subDirectory.GetFiles()) {
output = output + "<li><a href='" + file.FullName + "'>" + file.Name + "</a></li>";
}
output = output + "</ul>";
}
output = output + "</li></ul>";
}
} catch (System.UnauthorizedAccessException) {
//This throws when we don't have access.
}
}
You should use System.Text.StringBuilder (Good performance) instead of string concatenate(Immutable) Bad performance.
You should use normal string replace function is not using complex search. subDirectory.Name.replace("_", " ");
Main reason for slowness in your code is most likely multiple calls to GetFiles and GetDirectories. You are calling them over and over again in if conditions as well as in your initial lookups. You only need the counts only once. Also, adding strings aren't helping the cause.
Following code was able to run through my simple usb-drive in 300ms and return with over 400 folders and 11000 files. On slow network drive, it was able to return in 9 seconds for 4000 files in 300 folders. It can probably be further optimized with Parallel.ForEach during recursion.
protected void Page_Load(object sender, EventArgs e) {
itemWrapper.InnerHtml = GetDirectory(Request.QueryString["path"]);
}
static string GetDirectory(string path)
{
StringBuilder output = new StringBuilder();
var subdir = System.IO.Directory.GetDirectories(path);
var files = System.IO.Directory.GetFiles(path);
output.Append("<ul><li><a>");
output.Append(path.Replace("_", " "));
output.Append(subdir.Length > 0 || files.Length > 0 ? "+</a>" : "</a>");
foreach(var sb in subdir)
{
output.Append(GetDirectory(sb));
}
if (files.Length > 0)
{
output.Append("<ul>");
foreach (var file in files)
{
output.AppendFormat("<li>{1}</li>", file, System.IO.Path.GetFileName(file));
}
output.Append("</ul>");
}
output.Append("</ul>");
return output.ToString();
}

will only copy first item in the array c#

Hi I am trying to write a simple program to copy a folder from one soure to many in parallel.
I am learning c# so have been trying to understand and change code examples, as i figured this the best way to learn somthing new.
The example below does not work as it only copies to the first destination in the destinationPaths
The stange thing is i have a simlar method to copy one file to many and this works everytime
have i missing something?? i would be greatful if someone could tell me why this is not working i am guessing that there maybe certain things you can't do in parallel
any advice would be great
public void CopyMultipleFolder(string sourceFilePath, params string[] destinationPaths)
{
if (string.IsNullOrEmpty(sourceFilePath)) MessageBox.Show("A source file must be specified.", "sourceFilePath");
else
{
if (destinationPaths == null || destinationPaths.Length == 0) MessageBox.Show("At least one destination file must be specified.", "destinationPaths");
else
{
try
{
FileIOPermission writeAccess = new FileIOPermission(FileIOPermissionAccess.AllAccess, destinationPaths);
foreach (string i in destinationPaths)
{
writeAccess.AddPathList(FileIOPermissionAccess.Write, i);
}
writeAccess.Demand();
NetworkCredential user = new NetworkCredential();
user.UserName = Properties.Settings.Default.username;
user.Password = Properties.Settings.Default.password;
if (user.Password.Length == 0 || user.UserName.Length == 0)
{
MessageBox.Show("No Username or password have been entered click username on menu bar to update", "Update Credentials");
}
else
{
Parallel.ForEach(destinationPaths, new ParallelOptions(),
destinationPath =>
{
if (sourceFilePath.EndsWith("*"))
{
int l = sourceFilePath.Length - 4;
sourceFilePath = sourceFilePath.Remove(l);
}
else
{
using (new NetworkConnection(destinationPath, user))
{
if (Directory.Exists(destinationPath + "\\" + foldername))
{
if (destinationPath.EndsWith("\\"))
{
DialogResult r = MessageBox.Show("Folder already Exists " + destinationPath + foldername + " Do You Want To overwrite All Files And Sub Folders", "Overwrite?", MessageBoxButtons.YesNo);
if (r == DialogResult.Yes)
{
PleaseWait.Create();
foreach (string dirPath in Directory.GetDirectories(sourceFilePath, "*", SearchOption.AllDirectories))
Directory.CreateDirectory(dirPath.Replace(sourceFilePath, destinationPath + "\\" + foldername));
foreach (string newPath in Directory.GetFiles(sourceFilePath, "*.*", SearchOption.AllDirectories))
File.Copy(newPath, newPath.Replace(sourceFilePath, destinationPath+ "\\" + foldername), true);
list = list + destinationPath + foldername + Environment.NewLine;
}
else
{
}
}
else
{
DialogResult r = MessageBox.Show("Folder already Exists " + destinationPath + "\\" + foldername + " Do you Want to overwrite All Files And SubFolders", "Overwrite?", MessageBoxButtons.YesNo);
if (r == DialogResult.Yes)
{
PleaseWait.Create();
foreach (string dirPath in Directory.GetDirectories(sourceFilePath, "*", SearchOption.AllDirectories))
Directory.CreateDirectory(dirPath.Replace(sourceFilePath, destinationPath + "\\" + foldername));
//Copy all the files
foreach (string newPath in Directory.GetFiles(sourceFilePath, "*.*", SearchOption.AllDirectories))
File.Copy(newPath, newPath.Replace(sourceFilePath, destinationPath + "\\" + foldername), true);
list = list + destinationPath + "\\" + foldername + Environment.NewLine;
}
else
{
}
}
}
else
{
PleaseWait.Create();
foreach (string dirPath in Directory.GetDirectories(sourceFilePath, "*", SearchOption.AllDirectories))
Directory.CreateDirectory(dirPath.Replace(sourceFilePath, destinationPath + "\\" + foldername));
//Copy all the files
foreach (string newPath in Directory.GetFiles(sourceFilePath, "*.*", SearchOption.AllDirectories))
File.Copy(newPath, newPath.Replace(sourceFilePath, destinationPath + "\\" + foldername), true);
list = list + destinationPath +"\\"+foldername+ Environment.NewLine;
}
}
}
PleaseWait.Destroy();
});
MessageBox.Show("Folder Has Been Copied to " + list, "Folder Copied");
}
}
catch (UnauthorizedAccessException uae)
{
MessageBox.Show(uae.ToString());
}
}
}
}
You wrote you were learning C#. So, forget about parallel execution, because it unnecessarily makes your task more complicated. Instead, start by decomposing your problem into smaller parts. The code you posted is ugly, long, repeats a lot of logic many times, and hence it is and will be hard to read, debug, and maintain.
So, start by writing small functions for individual files. You need to create a set of folders in a destination folder. Hence write a function accepting a list of names and the destination folder. You need to determine the set of folders from a source folder. So write a function which does that. The combine those two functions together. And so on.
You will end up with a much cleaner, modifiable, reusable solution. Then it will be a lot easier to plug in parallel processing. Most likely, this will be for the sake of learning it, because it makes not much sense to parallelize your problem too heavily.

Categories

Resources