Move File To Sub-Directories - c#

Moving files is easy & clear in .NET what gets me is how to move to a sub-directory For example, let's say I have a file in my source destination called Joe_2.mp3, and I need to move it to destination directory and to the most recently modified sub-folder if one exists. If it does not just move it straight into the destination directory. How would I 1st check for sub-directories & if they exist find the most recently modified one and move the source file their?
This is what I was using when it was just a straight move from directory1 to directory2
private string source = #"C:\\First\\";
private string unclear = #"C:\\Second\\";
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
DirectoryInfo FirstCheck = new DirectoryInfo(#"C:\\First\\");
DirectoryInfo SecondCheck = new DirectoryInfo(#"C:\\Second\\");
IEnumerable<string> list1 = FirstCheck.GetFiles("*.*").Select(fi => fi.Name);
IEnumerable<string> list2 = SecondCheck.EnumerateFiles("*", SearchOption.AllDirectories).Select(fi => fi.Name);
IEnumerable<string> missing = list1.Except(list2);
foreach (var file in missing)
{
var subdi = new DirectoryInfo(SecondCheck.ToString());
var direcetories = subdi.EnumerateDirectories().OrderBy(d => d.CreationTime).Select(d => d.Name).ToList();
File.Move(source, unclear);
}
}

Use Directory.GetDirectories to get the paths of all the subdirectories, then use the Directory.GetCreationTime to get the info you need. Then you can just sort them and take the minimal value that matches your criteria.

Related

Accessing files within child directories

I am trying to access the files in the images directory that lies within another directory but when I run my code it doesn't print out anything:
string path = #"C:\Path";
DirectoryInfo DFolder = new DirectoryInfo(path);
Collection cDetails = new Collection(DFolder);
string DFPath = DFolder.Name;
DirectoryInfo imDetails = new DirectoryInfo(imPath);
// Get Desired Directories
List<string> directoryFilter = new List<string> {"images", "videos", "RAW"};
List<DirectoryInfo> directoryList = DFolder
.GetDirectories("*", SearchOption.AllDirectories)
.Where(x => directoryFilter.Contains(x.Name.ToLower()))
.ToList();
string dpath = directoryList.ToString();
foreach (DirectoryInfo record in directoryList)
{
foreach (FileInfo file in record.GetFiles(#"*", SearchOption.TopDirectoryOnly))
{
Console.WriteLine(file); //It compiles but doesn't print anything on the console
}
}
Note: This isn't really an answer, so I'll delete it shortly, but wanted to give some sample code to test with in case it helps.
Your code works fine for me, so it seems that the problem is that either the directories don't exist, or they don't contain any files.
Here's a test program you can run which creates a bunch of directories under c:\temp, some of which have the names we care about. The names we care about are also found at different levels of depth in the path, yet they are all discovered:
static void CreateTestPathsAndFiles()
{
// Paths to create for testing. We will put a file in each directory below,
// but our search code should only print the file paths of those files that
// are directly contained in one of our specially-named folders
var testPaths = new List<string>
{
#"c:\temp\dummy1",
#"c:\temp\dummy1\raw", // This should print
#"c:\temp\dummy2",
#"c:\temp\dummy2\extra",
#"c:\temp\dummy3",
#"c:\temp\dummy3\dummy31",
#"c:\temp\dummy3\dummy32\raw", // This should print
#"c:\temp\extra",
#"c:\temp\images", // This should print
#"c:\temp\notUsed",
#"c:\temp\notUsed\videos", // This should print
#"c:\temp\raw", // This should print
#"c:\temp\videos\dummy1",
};
// Just something to make a unique file name
int fileId = 0;
// for testing, ensure that the directories exist and contain some files
foreach(var testPath in testPaths)
{
// Create the directory
Directory.CreateDirectory(testPath);
// Add a file to it
File.CreateText(Path.Combine(testPath, $"TempFile{fileId}.txt"))
.Write($"Dummy text in file {fileId}");
// Increment our file counter
fileId++;
}
}
static void Main(string[] args)
{
// Create our paths and files for testing
CreateTestPathsAndFiles();
// Now set our root directory, search for all folders matching our
// special folder names, and print out the files contained in them
var path = #"C:\Temp";
var directoryFilter = new List<string> {"images", "videos", "raw"};
// Get ALL sub-directories under 'path' whose name is in directoryFilter
var subDirectories = new DirectoryInfo(path)
.GetDirectories("*", SearchOption.AllDirectories)
.Where(x => directoryFilter.Contains(x.Name.ToLower()));
foreach (DirectoryInfo subDir in subDirectories)
{
foreach (FileInfo file in subDir.GetFiles(#"*", SearchOption.TopDirectoryOnly))
{
// We're using the FullName so we see the whole file path in the output
Console.WriteLine(file.FullName);
}
}
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
Note that the 5 files we expected to find are listed, but no others:
foreach (FileInfo file in record.GetFiles(#"*", SearchOption.TopDirectoryOnly))
{
Console.WriteLine(file); //It compiles but doesn't print anything on the console
}
SearchOption.TopDirectoryOnly will only search files in C://Path/images but not its subfolders.
a possible fix for this is to simply change your 2nd foreach loop to look like this:
foreach (FileInfo file in record.GetFiles(#"*", SearchOption.AllDirectories))
{
Console.WriteLine(file); //It compiles but doesn't print anything on the console
}
Edit:
Using SearchOption.AllDirectories as parameter is supposed to catch all cases of subfolders within your subfolder e.g. something like C://images//dir//somefile.txt instead of only taking the files within the topdirectory(in this case C://images). Which is(as i understood your question) exactly the kind of behaviour you were looking for.
Full code:
{
static void Main(string[] args)
{
// Directory Info
string path = #"C:\Path";
DirectoryInfo DFolder = new DirectoryInfo(path);
string DFPath = DFolder.Name;
// Get Desired Directories
List<string> directoryFilter = new List<string> { "images", "videos", "raw" };
List<DirectoryInfo> directoryList = DFolder.GetDirectories("*", SearchOption.AllDirectories).Where(x => directoryFilter.Contains(x.Name.ToLower())).ToList();
string dpath = directoryList.ToString();
foreach (DirectoryInfo record in directoryList)
{
foreach (FileInfo file in record.GetFiles(#"*", SearchOption.AllDirectories)) //searches directory record and its subdirectories
{
Console.WriteLine(file);
}
}
}
Final Edit: Sample output given the following structure:
C://Path//images//images.somefile.txt
C://Path//images//temp//images.temp.somefile.txt
C://Path//raw//raw.somefile.txt
C://Path//vidoes//videos.somefile.txt

Try to load a picture from an array of pictures

I'm trying to load a picture into a pictureBox from a string array of pictures I created using Directory.GetFiles(). I believe I am not setting properly setting the picFile correctly.
I've than created a pictureBox_Click event to load subsequent pictures but have not written that event handler
string fileEntries = "";
private void showButton_Click(object sender, EventArgs e)
{
// First I want the user to be able to browse to and select a
// folder that the user wants to view pictures in
string folderPath = "";
FolderBrowserDialog folderBrowserDialog1 = new FolderBrowserDialog();
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
folderPath = folderBrowserDialog1.SelectedPath;
}
// Now I want to read all of the files of a given type into a
// array that from the path provided above
ProcessDirectory(folderPath);
// after getting the list of path//filenames I want to load the first image here
string picFile = fileEntries;
pictureBox1.Load(picFile);
}
public static void ProcessDirectory(string targetDirectoy)
{
// Process the list of files found in the directory.
string[] fileEntries = Directory.GetFiles(targetDirectoy);
}
// event handler here that advances to the next picture in the list
// upon clicking
}
If I redirect the string array to the Console I see the list of files in that directory but it also has the full path as part of the string - not sure if that is the issue.
string[] fileEntries = ProcessDirectory(folderPath);
if (fileEntries.Length > 0) {
string picFile = fileEntries[0];
pictureBox1.Load(picFile);
}
You have fileEntries declared twice.
public static string[] ProcessDirectory(string targetDirectoy) {
return Directory.GetFiles(targetDirectoy);
}
Now I want to read all of the files of a given type into a
array that from the path provided above
So you have to change the signature of the method ProcessDirectory to return a string affy that includes all the picture files, you can use the search pattern to get files with specific extension as well. You can use the following signature:
public static string[] ProcessDirectory(string targetDirectoy)
{
return Directory.GetFiles(targetDirectoy,"*.png");
}
after getting the list of path//filenames I want to load the first image here
So you can call the method to get all files in that specific directory with specific extensions. And then load the first file to the picturebox if the array having any files, you can use the following code for this:
var pictureFiles = ProcessDirectory(folderPath);
if (pictureFiles.Length > 0)
{
// process your operations here
pictureBox1.Load(pictureFiles[0]);
}

Copy entire directory with button from two dynamic locations

Okay, I have a button (button1) which I want to copy the static directory to the chosen directory. Essentially I have textbox1 in which different numeric values are added which correlate with different directories. I have a dictionary that sets the string to mapping which links to codes from textbox2 to the path of the origination folder. . This determines where we copy our data from. I want this data to then be copied into the folder selected in textbox2 through the folderBrowserDialog1.ShowDialog(); command. how to i create the dictionary and where do i put it for textbox1, and how do i then get the button to take whatever is in textbox1 and copy the entire directory to textbox2?
private Dictionary<string, string> mapping = new Dictionary<string, string>
{
{ "111", #"C:\Program Files\Example" },
{ "112", #"C:\Program Files\Example2" },
{ "113", #"C:\Program Files\Example3" },
};
public static string[] GetFiles(string mapping);
public static void Copy(string sourceFileName, string destFileName);
private void button2_Click(object sender, EventArgs e)
{
string destination = textBox1.Text;
foreach (var f in Directory.GetFiles(mapping))
{
File.Copy(Path.Combine(mapping, f)); destination;
}
}
Here is an answeer to "How copy an entire directory of files":
Use Directory.GetFiles() (see documentation) to get a list of all files in a directory.
Then use File.Copy() (see documenation) to copy a single file:
foreach(var f in Directory.GetFiles(srcPath))
{
File.Copy(Path.Combine(srcPath, f), dstPath);
}
EDIT
Directory.GetFiles() requires a path:
private void button2_Click(object sender, EventArgs e)
{
string destination = textBox1.Text;
string srcdir = mapping["111"];
foreach(var f in Directory.GetFiles(srcdir))
{
string srcpath = Path.Combine(srcdir, f)
File.Copy(srcpath, destination);
}
}
I'm not 100% sure I understood the details of your question, as the uses of TextBox1 and TextBox2 don't seem consistent, but here's what I read:
TextBox1 has the codes that the dictionary maps to a source directory.
TextBox2 has the path (from the dialog box) to the destination directory.
If that is correct, you're close. Some things to note:
I'm not sure why you have these two lines. They look like method definitions, but there's no implementation. I think you can remove them and use the equivalent System.IO calls:
public static string[] GetFiles(string mapping);
public static void Copy(string sourceFileName, string destFileName);
Directory.GetFiles(mapping) won't work, because mapping is a Dictionary<string, string>, not a string. You need to select the corresponding value (path) based on the key (numeric code) from TextBox1 and use that in the Directory.GetFiles() method.
File.Copy(Path.Combine(mapping, f)); destination; is an incorrect syntax and won't compile (should be File.Copy(Path.Combine(mapping, f), destination);. Additionally, you don't need to combine the source path and filename for the first argument, as GetFiles returns the path along with the filename (including extension). You will need to get the filename alone and combine it with the sourece path for the destination file, however. You can use Path.GetFileName to do this.
Try this code below - it addresses the issues noted above, with the assumption being that textBox1 is the source in mappings and textBox2 is the destination from the dialog window:
private void button2_Click(object sender, EventArgs e)
{
string source = textBox1.Text
string destination = textBox2.Text;
if (mappings.ContainsKey(source))
{
foreach (var f in Directory.GetFiles(source))
{
File.Copy(f, Path.Combine(destination, Path.GetFileName(f)));
}
}
else
{
// No key was found, how you handle it will be dictated by the needs of the application and/or users.
}
}
The above code does the following:
Gets the numeric value for the path of the source directory. This should correspond to one of the keys in the dictionary.
Gets the path for the destination directory based on the user selected value from the dialog box.
Checks to see if the key from textBox1 is present in the directory.
If it is, it gets the corresponding value for that key, which is the path for the source directory.
Next, it loops through the files in the specified source directory, copying each one in turn to the destination directory. Note that f (the file) is used as the source file for the first argument (as it contains the path as well, as Directory.GetFiles() returns both the path and the filename), and the destination directory is combined with the filename (retrieved by a call to Path.GetFileName). If f alone was used in the Path.Combine() method, you'd wind up with sourcePath\originalPath\filename.
If the key wasn't found, you ignore it or do something with it.
Again, the above code is based on my understanding of your question. If my understanding was less than correct, the principles I outlined should still be able to assist you in resolving the issue.
** EDIT **
Per the comments below, it sounds like all you need to do is flip the assignments for source and destination. textBox2 will contain the code that corresponds to a key in the mappings directory, which will give you the path of the source directory. textBox1 will contain the destination path. Like this:
string source = textBox2.Text
string destination = textBox1.Text;
The rest of the code stays the same.

Dynamic File Paths

Suppose I had a particular directory on C# in the following format:
#"C:\blabla\bla\0.0.1.63\blabla.png";
The "0.0.1.63" changes occasionally due to updates to the software and so on.
I want to know how I can assign a "..\" - similar effect for that particular directory to be dynamic. Because I do not know the update sequence.
So, how do I make it so that the directory stays the same, while that particular part of the directory (0.0.1.63) is an "unknown" directory.
You can use the DirectoryInfo.EnumerateDirectories and the DirectoryInfo.EnumerateFileSystemInfos methods to find your file, you could then strip the filename from the FileInfo objects FullName and use the result. Something like this should work.
private void button1_Click(object sender, EventArgs e)
{
openFileDialog1.InitialDirectory = getPath();
openFileDialog1.ShowDialog();
}
private string getPath()
{
DirectoryInfo di = new DirectoryInfo(#"C:\blabla\bla\");
foreach (var d in di.EnumerateDirectories())
{
foreach(var fi in d.EnumerateFileSystemInfos())
{
if (fi.Name == "blabla.png")
{
return fi.FullName.Replace(fi.Name,"");
}
}
}
return di.FullName ;
}

How do I compare one collection of files to another in c#?

I am just learning C# (have been fiddling with it for about 2 days now) and I've decided that, for leaning purposes, I will rebuild an old app I made in VB6 for syncing files (generally across a network).
When I wrote the code in VB 6, it worked approximately like this:
Create a Scripting.FileSystemObject
Create directory objects for the source and destination
Create file listing objects for the source and destination
Iterate through the source object, and check to see if it exists in the destination
if not, create it
if so, check to see if the source version is newer/larger, and if so, overwrite the other
So far, this is what I have:
private bool syncFiles(string sourcePath, string destPath) {
DirectoryInfo source = new DirectoryInfo(sourcePath);
DirectoryInfo dest = new DirectoryInfo(destPath);
if (!source.Exists) {
LogLine("Source Folder Not Found!");
return false;
}
if (!dest.Exists) {
LogLine("Destination Folder Not Found!");
return false;
}
FileInfo[] sourceFiles = source.GetFiles();
FileInfo[] destFiles = dest.GetFiles();
foreach (FileInfo file in sourceFiles) {
// check exists on file
}
if (optRecursive.Checked) {
foreach (DirectoryInfo subDir in source.GetDirectories()) {
// create-if-not-exists destination subdirectory
syncFiles(sourcePath + subDir.Name, destPath + subDir.Name);
}
}
return true;
}
I have read examples that seem to advocate using the FileInfo or DirectoryInfo objects to do checks with the "Exists" property, but I am specifically looking for a way to search an existing collection/list of files, and not live checks to the file system for each file, since I will be doing so across the network and constantly going back to a multi-thousand-file directory is slow slow slow.
Thanks in Advance.
The GetFiles() method will only get you files that does exist. It doesn't make up random files that doesn't exist. So all you have to do is to check if it exists in the other list.
Something in the lines of this could work:
var sourceFiles = source.GetFiles();
var destFiles = dest.GetFiles();
foreach (var file in sourceFiles)
{
if(!destFiles.Any(x => x.Name == file.Name))
{
// Do whatever
}
}
Note: You have of course no guarantee that something hasn't changed after you have done the calls to GetFiles(). For example, a file could have been deleted or renamed if you try to copy it later.
Could perhaps be done nicer somehow by using the Except method or something similar. For example something like this:
var sourceFiles = source.GetFiles();
var destFiles = dest.GetFiles();
var sourceFilesMissingInDestination = sourceFiles.Except(destFiles, new FileNameComparer());
foreach (var file in sourceFilesMissingInDestination)
{
// Do whatever
}
Where the FileNameComparer is implemented like so:
public class FileNameComparer : IEqualityComparer<FileInfo>
{
public bool Equals(FileInfo x, FileInfo y)
{
return Equals(x.Name, y.Name);
}
public int GetHashCode(FileInfo obj)
{
return obj.Name.GetHashCode();
}
}
Untested though :p
One little detail, instead of
sourcePath + subDir.Name
I would use
System.IO.Path.Combine(sourcePath, subDir.Name)
Path does reliable, OS independent operations on file- and foldernames.
Also I notice optRecursive.Checked popping out of nowhere. As a matter of good design, make that a parameter:
bool syncFiles(string sourcePath, string destPath, bool checkRecursive)
And since you mention it may be used for large numbers of files, keep an eye out for .NET 4, it has an IEnumerable replacement for GetFiles() that will let you process this in a streaming fashion.

Categories