I want to copy all files in a directory to a destination folder using Visual C# .NET version 3.5, which can be done pretty easily (taken from this answer):
private static void Copy(string sourceDir, string targetDir)
{
Directory.CreateDirectory(targetDir);
foreach (var file in Directory.GetFiles(sourceDir))
File.Copy(file, Path.Combine(targetDir, Path.GetFileName(file)));
foreach (var directory in Directory.GetDirectories(sourceDir))
Copy(directory, Path.Combine(targetDir, Path.GetFileName(directory)));
}
Now, there's one little problem here: I want it to sort all files smallest first, so if the source path is a removable drive, which gets plugged out after some time, it would still have most possible data copied. With upper algorithm, if it takes a directory containing big files first and continues with a directory containing many smaller ones afterwards, there is the chance that the user will plug his drive out while the software is still copying the big file, and nothing will stay on the drive except the incomplete big file.
My idea was to do multiple loops: First, every filepath would be put in a dictionary including its size, then this dictionary would get sorted, and then every file would be copied from source to destination (including folder creation).
I'm afraid this is not a very neat solution, since looping two times about the same just doesn't seem right to me. Also, I'm not sure if my dictionary can store that much information, if the source folder has got too many different files and subfolders.
Are there any better options to choose from?
You could use a simpler method based on the fact that you can get all the files in a directory subtree just asking for them without using recursion.
The missing piece of the problem is the file size. This information could be obtained using the DirectoryInfo class and the FileInfo class while the ordering is just a Linq instruction applied to the sequence of files as in the following example.
private static void Copy(string sourceDir, string targetDir)
{
DirectoryInfo di = new DirectoryInfo(sourceDir);
foreach (FileInfo fi in di.GetFiles("*.*", SearchOption.AllDirectories).OrderBy(d => d.Length))
{
string leftOver = fi.DirectoryName.Replace(sourceDir, "");
string destFolder = Path.Combine(targetDir, leftOver);
// Because the files are listed in order by size
// we could copy a file deep down in the subtree before the
// ones at the top of the sourceDir
// Luckily CreateDirectory doesn't throw if the directory exists
// and automatically creates all the intermediate folders required
Directory.CreateDirectory(destFolder);
// Just write the intended copy parameters as a proof of concept
Console.WriteLine($"{fi.Name} with size = {fi.Length} -> Copy from {fi.DirectoryName} to {Path.Combine(destFolder, fi.Name)}");
}
}
In this example I have changed the File.Copy method with a Console.WriteLine just to have a proof of concept without copying anything, but the substitution is trivial.
Notice also that it is better to use EnumerateFiles instead of GetFiles as explained in the MSDN documentation
I hope this helps!
First; get all files from the source directory, with recursion being optional. Then proceed to copy all files ordered by size to the target directory.
void CopyFiles(string sourceDir, string targetDir, bool recursive = false)
{
foreach (var file in GetFiles(sourceDir, recursive).OrderBy(f => f.Length))
{
var subDir = file.DirectoryName
.Replace(sourceDir, String.Empty)
.TrimStart(Path.DirectorySeparatorChar);
var fullTargetDir = Path.Combine(targetDir, subDir);
if (!Directory.Exists(fullTargetDir))
Directory.CreateDirectory(fullTargetDir);
file.CopyTo(Path.Combine(fullTargetDir, file.Name));
}
}
IEnumerable<FileInfo> GetFiles(string directory, bool recursive)
{
var files = new List<FileInfo>();
if (recursive)
{
foreach (var subDirectory in Directory.GetDirectories(directory))
files.AddRange(GetFiles(subDirectory, true));
}
foreach (var file in Directory.GetFiles(directory))
files.Add(new FileInfo(file));
return files;
}
Related
Is there a way to search for a string within a file(s) within a zipped folder WITHOUT unzipping the files?
My situation is I have over 1 million files zipped by months of the year.
For example 2008_01, 2008_02, etc.
I need to extract/unzip only the files with specific serial numbers within the files.
The only thing I can find is unzipping the data to a temporary location to perform that search, but it takes me 45-60 minutes just to unzip the data manually. So I assume the code would take just as long to perform that task, plus I don't have that much available space.
Please Help.
Unfortunately, there isn't a way to do this. The zip format maintains an uncompressed manifest that shows file names and directory structure, but the contents of the files themselves are compressed, and therefore any string inside a file won't match your search until the file is decompressed.
This same limitation exists with just about any general-purpose file compression format (7zip, gzip, rar, etc.). You're essentially reclaiming disk space at the expense of CPU cycles.
Using some extension methods, you can scan through the Zip files. I don't think you can gain anything by trying to scan a single zip in parallel, but you could probably scan multiple zip files in parallel.
public static class ZipArchiveEntryExt {
public static IEnumerable<string> GetLines(this ZipArchiveEntry e) {
using (var stream = e.Open()) {
using (var sr = new StreamReader(stream)) {
string line;
while ((line = sr.ReadLine()) != null)
yield return line;
}
}
}
}
public static class ZipArchiveExt {
public static IEnumerable<string> FilesContain(this ZipArchive arch, string target) {
foreach (var entry in arch.Entries.Where(e => !e.FullName.EndsWith("/")))
if (entry.GetLines().Any(line => line.Contains(target)))
yield return entry.FullName;
}
public static void ExtractFilesContaining(this ZipArchive arch, string target, string extractPath) {
if (!extractPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
extractPath += Path.DirectorySeparatorChar;
foreach (var entry in arch.Entries.Where(e => !e.FullName.EndsWith("/")))
if (entry.GetLines().Any(line => line.Contains(target)))
entry.ExtractToFile(Path.Combine(extractPath, entry.Name));
}
}
With these, you can search a zip file with:
var arch = ZipFile.OpenRead(zipPath);
var targetString = "Copyright";
var filesToExtract = arch.FilesContain(targetString);
You could also extract them to a particular path (assuming no filename conflicts) with:
var arch = ZipFile.OpenRead(zipPath);
var targetString = "Copyright";
arch.ExtractFilesContaining(targetString, #"C:\Temp");
You could modify ExtractFilesContaining to e.g. add the year-month to the file names to help avoid conflicts.
I am trying to enumerate the zipped folders that are inside an unzipped folder using Directory.GetDirectories(folderPath).
The problem I have is that it does not seem to be finding the zipped folders, when I come to iterate over the string[], it is empty.
Is Directory.GetDirectories() the wrong way to go about this and if so what method serves this purpose?
Filepath example: C:\...\...\daily\daily\{series of zipped folder}
public void CheckZippedDailyFolder(string folderPath)
{
if(folderPath.IsNullOrEmpty())
throw new Exception("Folder path required");
foreach (var folder in Directory.GetDirectories(folderPath))
{
var unzippedFolder = Compression.Unzip(folder + ".zip", folderPath);
using (TextReader reader = File.OpenText(unzippedFolder + #"\" + new DirectoryInfo(folderPath).Name))
{
var csv = new CsvReader(reader);
var field = csv.GetField(0);
Console.WriteLine(field);
}
}
}
GetDirectories is the wrong thing to use. Explorer lies to you; zip files are actually files with an extension .zip, not real directories on the file system level.
Look at:
https://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive.entries%28v=vs.110%29.aspx (ZipArchive.Entries) and/or
https://msdn.microsoft.com/en-us/library/system.io.compression.zipfile%28v=vs.110%29.aspx (ZipFile) to see how to deal with them.
I am using .NET 2.0 and Linq is out of question. I would like to check if file exists inside a directory without knowledge of the file extension.
I just need this logic done.
1.Check File exists in Directory using String Filename provided using search pattern leaving out the extension of the File
2.Get the Files if they exists and Databind to provide Download links.If file does not exist then start uploading the File.
Update:
Directory.GetFiles() and DirectoryInfo.GetFiles() indeed solves the part where in i check for File existence. As for the performance regarding FileInfo objects, these were only solution to my requirements of databinding to provide download links
DirectoryInfo root = new DirectoryInfo("your_directory_path");
FileInfo[] listfiles = root.GetFiles("dummy.*");
if (listfiles.Length > 0)
{
//File exists
foreach (FileInfo file in listfiles)
{
//Get Filename and link download here
}
}
else
{
//File does not exist
//Upload
}
Hope this works
To see if a file exists with that name, can you not just use..
However, Directory.GetFiles already includes the full path
string [] files = Directory.GetFiles(Path,"name*");
bool exists = files.Length > 0;
if ( exists)
{
//Get file info - assuming only one file here..
FileInfo fi = new FileInfo(files[0]);
//Or loop through all files
foreach (string s in files)
{
FileInfo fi = new FileInfo(s);
//Do something with fileinfo
}
}
You can use DirectoryInfo.GetFiles() to have a FileInfo[] instead of a String[].
I have 800-1000 Uniquely named folders in one large folder.
Inside of each uniquely named folder is another folder called /images.
And inside of each image folder is a file named "Rock-Star-Site-Design-{UNIQUEFOLDERNAME}-ca-logo.png"
I write a code that replaces all .png files (while keeping the original name) from a .png file which I supply.
The folder structure and filenames need to remain the same. Basically i am updating the old file with a new file, using the same (unique) name, 800-1000 times.
The code i have tried working properly but there is one mistake.There are plenty of images inside Image folder,But i need to update only "Rock-Star-Site-Design-{UNIQUEFOLDERNAME}-ca-logo.png"file every folder.
is there any way so i can get file.startwith("Rock-Star").So i can update particular file i want.
Here is my code:
private List<String> DirSearch(string sDir)
{
List<String> files = new List<String>();
try
{
foreach (string f in Directory.GetFiles(sDir))
{
files.Add(f);
}
foreach (string d in Directory.GetDirectories(sDir))
{
files.AddRange(DirSearch(d));
}
foreach (var file in files)
{
if (!string.IsNullOrWhiteSpace(file))
{
File.Copy(Server.MapPath("ca-logo.jpg"), file,true);
}
}
}
catch (System.Exception excpt)
{
//MessageBox.Show(excpt.Message);
}
return files;
}
You can use this to get files based on a regular expression
Directory.GetFiles(sDir, "Rock-Star*.png");
Rock-Star*.png means files starting with Rock-Star, * means any character or sequence of characters, ending with .png
First get the file name from file full path using Path.GetFileName(). Then Use StartsWith(). When you add files to the list check if the file name starts with the name you want and then add to the list.
List<String> files = new List<String>();
foreach (string f in Directory.GetFiles(""))
{
//Get file name from full file path
string fileName = Path.GetFileName(f);
//Get only the files starts with Rock-Star
if (fileName.StartsWith("Rock-Star"))
{
files.Add(f);
}
}
EDIT
If you want to update the files with both upper and lower case lettes then you have to pass StringComparison.OrdinalIgnoreCase enumeration to your StartsWith() method
//Get only the files starts with Rock-Star or rock-start
if (fileName.StartsWith("Rock-Star",StringComparison.OrdinalIgnoreCase))
{
files.Add(f);
}
EDIT
To get files within all sub directories and replace, Pass SearchOption.AllDirectories to the GetFiles() method. Following code will search all the sub directories for files with .png extension
foreach (string f in Directory.GetFiles(sDir, "*.png", SearchOption.AllDirectories))
I am looking to create a program that finds all files of a certain type on my desktop and places them into specific folders, for example, I would have all files with .txt into the Text folder.
Any ideas what the best way would be to accomplish this? Thanks.
I have tried this:
string startPath = #"%userprofile%/Desktop";
string[] oDirectories = Directory.GetDirectories(startPath, "");
Console.WriteLine(oDirectories.Length.ToString());
foreach (string oCurrent in oDirectories)
Console.WriteLine(oCurrent);
Console.ReadLine();
It was not successful in finding all of the files.
A lot of these answers won't actually work, having tried them myself. Give this a go:
string filepath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
DirectoryInfo d = new DirectoryInfo(filepath);
foreach (var file in d.GetFiles("*.txt"))
{
Directory.Move(file.FullName, filepath + "\\TextFiles\\" + file.Name);
}
It will move all .txt files on the desktop to the folder TextFiles.
First off; best practice would be to get the users Desktop folder with
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
Then you can find all the files with something like
string[] files = Directory.GetFiles(path, "*.txt", SearchOption.AllDirectories);
Note that with the above line you will find all files with a .txt extension in the Desktop folder of the logged in user AND all subfolders.
Then you could copy or move the files by enumerating the above collection like
// For copying...
foreach (string s in files)
{
File.Copy(s, "C:\newFolder\newFilename.txt");
}
// ... Or for moving
foreach (string s in files)
{
File.Move(s, "C:\newFolder\newFilename.txt");
}
Please note that you will have to include the filename in your Copy() (or Move()) operation. So you would have to find a way to determine the filename of at least the extension you are dealing with and not name all the files the same like what would happen in the above example.
With that in mind you could also check out the DirectoryInfo and FileInfo classes.
These work in similair ways, but you can get information about your path-/filenames, extensions, etc. more easily
Check out these for more info:
http://msdn.microsoft.com/en-us/library/system.io.directory.aspx
http://msdn.microsoft.com/en-us/library/ms143316.aspx
http://msdn.microsoft.com/en-us/library/system.io.file.aspx
You can try with Directory.GetFiles and fix your pattern
string[] files = Directory.GetFiles(#"c:\", "*.txt");
foreach (string file in files)
{
File.Copy(file, "....");
}
Or Move
foreach (string file in files)
{
File.Move(file, "....");
}
http://msdn.microsoft.com/en-us/library/wz42302f