Is there a simple way to display the files in a folder to a CSV that shows whether the files in that folder are JPG, PDF, or other using C#?
There is no built-in way to do this in C#, but you can use the System.IO.Directory and System.IO.File classes to get a list of files in a directory and then check their extensions to see if they are JPG, PDF, or other.
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
string directory = "C:\\";
string[] files = Directory.GetFiles(directory);
using (StreamWriter writer = new StreamWriter("files.csv"))
{
foreach (string file in files)
{
string extension = Path.GetExtension(file);
writer.WriteLine("{0},{1}", file, extension);
}
}
}
}
You can use Matcher to traverse a folder structure using wild cards to getting specific file types and exclusions also.
Example method
public static async Task Demo(string parentFolder, string[] patterns, string[] excludePatterns, string fileName)
{
StringBuilder builder = new StringBuilder();
Matcher matcher = new();
matcher.AddIncludePatterns(patterns);
matcher.AddExcludePatterns(excludePatterns);
await Task.Run(() =>
{
foreach (string file in matcher.GetResultsInFullPath(parentFolder))
{
builder.AppendLine(file);
}
});
await File.WriteAllTextAsync("TODO", builder.ToString());
}
Example call, find all png and pdf files but exclude some specified in the exclude array which is explained in the docs.
string[] include = { "**/*.png", "**/*.pdf" };
string[] exclude = { "**/*in*.png", "**/*or*.png" };
await GlobbingOperations.Demo(
"Parent folder",
include,
exclude,
"Dump.txt");
When traversing files add any other logic to get specifics about a file and use that to add to the builder variable for writing to a file.
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 have files stored in multiple different folders and I need to make a ZIP archive from all the files in those folders. I have created a simple function using System.IO.Compression, that takes the data from just one folder and makes a ZIP archive, but I can't figure out how to do that for multiple folders. No folders needed in ZIP, just the files from it.
If it can't be done in this library, I can use a different one like DotNetZip or similar.
string folder1 = #"c:\ex\ZipFolder1";
string zipPath = #"c:\ex\AllFiles.zip";
ZipFile.CreateFromDirectory(folder1, zipPath);
I think you're using DotNetZip.Just create a ZipFile and add files by the method AddFiles
using (var file = new ZipFile())
{
//fileNames is an array containing the paths of the files from differents folders
file.AddFiles(fileNames);
file.Save(zipFile);
}
Have you tried using AddFile() method of ZipFile Class. Here you can replace 'PathOfFiles' dynamically as per your requirement.
var fileNames = new string[] { "a.txt", "b.xlsx", "c.png" };
using (var zip = new ZipFile()){
foreach (var file in fileNames)
zip.AddFile(#"PathOfFiles\" + fileName, "");
zip.Name = "ZipFile";
var pushStreamContent = new PushStreamContent((stream, content, context) =>
{
zip.Save(stream);
stream.Close();
}, "application/zip");
}
for anyone encountering this today, here's an updated short and simple answer based on Microsoft's docs:
https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.zipfileextensions.createentryfromfile?view=net-6.0
static void SaveFilesToZip(string zipTargetPath, string[] filePaths)
{
using var newZip = ZipFile.Open(zipTargetPath, ZipArchiveMode.Create);
foreach (var filePath in filePaths)
{
newZip.CreateEntryFromFile(filePath, Path.GetFileName(filePath));
}
}
I am looking to read folders and files from a directory structure like so..
e.g
C:\RootFolder
SubFolder1
SubFolder2
File1
File2
SubFolder3
File3
Files....
Files....
I would like to read both, files and folders and write to another directory I cant use copy , because the directory I want to write to is remote and not local.
I read the files here.... Id love to be able to read folders and files and write both to another directory.
public static IEnumerable<FileInfo> GetFiles(string dir)
{
return Directory.EnumerateFiles(dir, "*", SearchOption.AllDirectories)
.Select(path =>
{
var stream = File.OpenRead(path);
return new FileInfo(Path.GetFileName(path), stream);
})
.DisposeEach(c => c.Content);
}
this function writes files to a remote sftp site.
public Task Write(IEnumerable<FileInfo> files)
{
return Task.Run(() =>
{
using (var sftp = new SftpClient(this.sshInfo))
{
sftp.Connect();
sftp.ChangeDirectory(this.remoteDirectory);
foreach (var file in files)
{
sftp.UploadFile(file.Content, file.RelativePath);
}
}
});
}
In this function I write the read files from the above function.
private async static Task SendBatch(Config config, Batch batch, IRemoteFileWriter writer)
{
var sendingDir = GetClientSendingDirectory(config, batch.ClientName);
Directory.CreateDirectory(Path.GetDirectoryName(sendingDir));
Directory.Move(batch.LocalDirectory, sendingDir);
Directory.CreateDirectory(batch.LocalDirectory);
//Use RemoteFileWriter...
var files = GetFiles(sendingDir);
await writer.Write(files).ContinueWith(t =>
{
if(t.IsCompleted)
{
var zipArchivePath = GetArchiveDirectory(config, batch);
ZipFile.CreateFromDirectory(
sendingDir,
zipArchivePath + " " +
DateTime.Now.ToString("yyyy-MM-dd hh mm ss.Zip")
);
}
});
}
Thank you!
You are getting UnauthorizedAccessException: Access to the path 'C:\temp' is denied. because you can't open a stream from a folder as it doesn't contain bytes.
From what I can understand you are looking to copy the files from one folder to another.
This answer seems to cover what you are doing. https://stackoverflow.com/a/3822913/3634581
Once you have copied the directories you can then create the zip file.
If you don't need to copy the files and just create the Zip I would recommend that since it will reduce the disk IO and speed up the process.
ZipArchive (https://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive(v=vs.110).aspx) can be used to create a zip file straight to a stream.
I figured it out here is the solution
public Task Write(IEnumerable<FileInfo> files)
{
return Task.Run(() =>
{
using (var sftp = new SftpClient(this.sshInfo))
{
sftp.Connect();
sftp.ChangeDirectory(this.remoteDirectory);
foreach (var file in files)
{
var parts = Path.GetDirectoryName(file.RelativePath)
.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
sftp.ChangeDirectory(this.remoteDirectory);
foreach (var p in parts)
{
try
{
sftp.ChangeDirectory(p);
}
catch (SftpPathNotFoundException)
{
sftp.CreateDirectory(p);
sftp.ChangeDirectory(p);
}
}
sftp.UploadFile(file.Content, Path.GetFileName(file.RelativePath));
}
}
});
}
****Key point to the solution was
this
var parts = Path.GetDirectoryName(file.RelativePath)
.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
We call *Path.GetDirectoryName
on the file itself to get the directory that correlates to the file.
We split the file directory to get the folder name and finally create the folder name we obtained from the split and upload the file to it.
I hope this helps others who may encounter such issue.
I want to know how to read multiple (about 500-1000) text files which are located on a server.
So far, I've written code for a program that only reads a single text file.
Here's how I'm currently reading a single file.
public void button1_Click(object sender, EventArgs e)
{
// Reading/Inputing column values
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string[] fileLines = File.ReadAllLines(ofd.FileName);
I would like to get rid of the open file dialog box, and let the program automatically read the 500-1000 text files where are located in the sever.
I'm thinking something along the lines of
for (int i =0; i<numFiles; i++)
{
//just use string[] fileLines =File.ReadAllLines()
//how would i specify the path for multiple files?
}
Questions are then:
How would I approach this?
How exactly should I get the number of files?
(I'm guessing I'd have to read the server file which contains them.)
You can use Directory.GetFiles(string path) to get all files from a certain directory. You can then use a foreach loop to iterate through all the files in that directory and do your processing.
You can use recursion to loop through all directories. Using Directory.EnumerateFiles also allows you to use a foreach loop so you don't have to worry about the file count.
private static void ReadAllFilesStartingFromDirectory(string topLevelDirectory)
{
const string searchPattern = "*.txt";
var subDirectories = Directory.EnumerateDirectories(topLevelDirectory);
var filesInDirectory = Directory.EnumerateFiles(topLevelDirectory, searchPattern);
foreach (var subDirectory in subDirectories)
{
ReadAllFilesStartingFromDirectory(subDirectory);//recursion
}
IterateFiles(filesInDirectory, topLevelDirectory);
}
private static void IterateFiles(IEnumerable<string> files, string directory)
{
foreach (var file in files)
{
Console.WriteLine("{0}", Path.Combine(directory, file));//for verification
try
{
string[] lines = File.ReadAllLines(file);
foreach (var line in lines)
{
//Console.WriteLine(line);
}
}
catch (IOException ex)
{
//Handle File may be in use...
}
}
}
Also note Directory.EnumerateFiles provides overload that lets you provide a search pattern to narrow the scope of the files.
How you go about getting your files depends on if they are all located in the same directory of if you'll need to recursively search through a directory and all child directories. Directory.GetFiles is where you want to start. It has 3 overloads seen here. So you might try something like this:
string path = "\mypath\tosomehwere";
string searchPattern = "*.txt";
string[] MyFiles = Directory.GetFiles(path, searchPattern, SearchOption.AllDirectories);
Then just loop through the string array and proccess each file as you would normally.
foreach (string filePath in MyFiles)
{
MyFileProcessMethod(filePath)
}
Path.GetFileName(filePath) will return the individual text file name should you need it for your processing requirements.
My requirement is to read a file location retrieve the list of .jpg & .xml files along with their timestamp and write it to a file.
I am new to C#, so far i have been able to get the file list and write the output to a file, but i am not sure how to get the time stamp of file and write it along with list.
I have added code existing code, i would need to have a timestamp for every file in list so that I can use these details for a comparison downstream.
Please advise.
Code
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
class Program
{
static void Main()
{
TextWriter tw = new StreamWriter(#"C:\test\logfile_c#.txt");
// Put all xml file names in directory into array.
string[] array1 = Directory.GetFiles(#"C:\test","*.xml");
// Put all jpg files in directory into array.
string[] array2 = Directory.GetFiles(#"C:\test", "*.jpg");
// Display all XML files and write to text file.
Console.WriteLine("--- XML Files: ---");
foreach (string name in array1)
{
Console.WriteLine(name);
tw.WriteLine(name);
Console.ReadLine();
}
// Display all JPG files and write to text file..
Console.WriteLine("--- JPG Files: ---");
foreach (string name in array2)
{
Console.WriteLine(name);
tw.WriteLine(name);
Console.ReadLine();
}
tw.Close();
}
}
Output
C:\test\chq.xml
C:\test\img_1.jpg
C:\test\img_2.jpg
Depends which timestamp you're after. You can get the creation time using:
new FileInfo(filename).CreationTime;
That'll give you a DateTime. So you can just do:
tw.WriteLine(new FileInfo(name).CreationTime.ToString());
The .ToString() is optional - you can use it to control the date/time format used in your output. There's also a modified time property you can use if you want that instead.
As you said you've just started learning I've put together this slightly modified code to show you where you can simplify some things - I've also removed reading and writing to the Console.
using System.IO;
namespace Test
{
class Program
{
static void Main(string[] args)
{
// try to name your variables in a meaningful way.
string[] xmlFiles = Directory.GetFiles(#"C:\test", "*.xml");
string[] jpgFiles = Directory.GetFiles(#"C:\test", "*.jpg");
// File.CreateText creates a new file and returns a stream writer.
// wrap it in a using statement so you don't have to worry about closing it yourself
using (var writer = File.CreateText(#"C:\test\logfile_c#.txt"))
{
FileInfo fi;
foreach (string name in xmlFiles)
{
// FileInfo instances will give you access to properties
// of the file, including the creation date and time.
fi = new FileInfo(name);
// Use an overload of WriteLine using a format string.
writer.WriteLine("file name: {0}, creation time: {1}", name, fi.CreationTime);
}
foreach (string name in jpgFiles)
{
fi = new FileInfo(name);
writer.WriteLine("file name: {0}, creation time: {1}", name, fi.CreationTime);
}
}
}
}
}
You can try writing code as below:
//Select directory
DirectoryInfo dirInfo = new DirectoryInfo("D:\\test");
//Get .xml files
FileInfo[] xmlFiles = dirInfo.GetFiles("*.xml");
//Get .jpg files
FileInfo[] jpgFiles = dirInfo.GetFiles("*.jpg");
//Merge files together for convenience
List<FileInfo> allFiles = new List<FileInfo>();
allFiles.InsertRange(allFiles.Count, xmlFiles);
allFiles.InsertRange(allFiles.Count, jpgFiles);
//Loop through all files and write their name and creation time to text file
using (StreamWriter writer = new StreamWriter("D:\\test\\test.txt"))
{
foreach (FileInfo currentFile in allFiles)
{
//Format string in desired format
string info = string.Format("Name: {0} Creation time: {1}", currentFile.Name, currentFile.CreationTime);
writer.WriteLine(info);
}
}