Hope to not get boring with my Mailkit questions but I preffer asking them here in order to help others in the future if they need this help as well.
I need a method for searching a folder. I basically check if it exists and I'm intended to obviously open in if I need to work on it. The thing is that depending on the mailserver this could get a little messy because not every mailserver allows to create folders on the first level and so on (sigh).
Is there any way to recursively search for a folder and get its MailFolder object?
This is my actual code, which is pretty messy, "should" work in just level 2 folder and fails in carpeta.GetSubfolders() because I'm blowing my mind with folders, subfolers and where could I use the .Open method.
I actually have a method to check if the folder exists (the following one) and another to open it, so one problem calls to another :'D
private bool ExisteCarpeta(string nombreCarpetaABuscar)
{
try
{
imap.Inbox.Open(FolderAccess.ReadOnly);
var toplevel = imap.GetFolder(imap.PersonalNamespaces[0]);
var carpetasNivel1 = toplevel.GetSubfolders();
var carpeta = carpetasNivel1.FirstOrDefault(x => x.FullName.Equals(nombreCarpetaABuscar, StringComparison.InvariantCultureIgnoreCase));
carpeta.GetSubfolders();
return carpeta != null;
}
catch (Exception ex)
{
string mensaje = "Ha habido un problema comprando la existencia de la carpeta en el correo. \n";
throw new Exception(mensaje, ex);
}
}
You could do something like this:
static IMailFolder FindFolder (IMailFolder toplevel, string name)
{
var subfolders = toplevel.GetSubfolders ().ToList ();
foreach (var subfolder in subfolders) {
if (subfolder.Name == name)
return subfolder;
}
foreach (var subfolder in subfolders) {
var folder = FindFolder (subfolder, name);
if (folder != null)
return folder;
}
return null;
}
You could use the above method like this:
var toplevel = imap.GetFolder (imap.PersonalNamespaces[0]);
var sent = FindFolder (toplevel, "Sent Items");
Related
I'm doing a console project whose goal is to search the entire disk for all files with the extension '.config'
I've tried something like:
foreach (string file in Directory.GetFiles("C:\\", "*.config", SearchOption.AllDirectories))
{
Console.WriteLine(file);
Console.ReadLine();
}
but gave me an error "denied access to path (...)".
On the internet I found this code:
Stack<string> pending = new Stack<string>();
pending.Push("C:\\");
while (pending.Count != 0)
{
var path = pending.Pop();
string[] next = null;
try
{
next = Directory.GetFiles(path, "*.config");
}
catch { }
if (next != null && next.Length != 0)
foreach (var file in next)
{
Console.WriteLine(file);
Console.ReadLine();
}
try
{
next = Directory.GetDirectories(path);
foreach (var subdir in next) pending.Push(subdir);
}
catch { }
}
but it just shows the path clicking always in 'enter' and I want to save those files/path in a list.
Someone can help?
There are two things you can do to improve that code:
Use Directory.EnumerateFiles() and Directory.EnumerateDirectories() to avoid making a copy of the names of all the files in each directory.
Make the return type of the method IEnumerable<string> to make it easier to consume.
We also need to be very careful about exceptions caused by attempting to access protected files and directories. The code below is also complicated by the fact that you're not allowed to yield return from inside a try/catch block, so we have to rearrange the code somewhat.
(Also note that we have to dispose the enumerator returned from .GetEnumerator(); normally this is done automatically when you use foreach, but in this case we can't - because of having to avoid doing yield return in a try/catch - so we have to use using to dispose it.)
Here's a modification of your original code to do this:
public static IEnumerable<string> GetFiles(string root, string spec)
{
var pending = new Stack<string>(new []{root});
while (pending.Count > 0)
{
var path = pending.Pop();
IEnumerator<string> fileIterator = null;
try
{
fileIterator = Directory.EnumerateFiles(path, spec).GetEnumerator();
}
catch {}
if (fileIterator != null)
{
using (fileIterator)
{
while (true)
{
try
{
if (!fileIterator.MoveNext()) // Throws if file is not accessible.
break;
}
catch { break; }
yield return fileIterator.Current;
}
}
}
IEnumerator<string> dirIterator = null;
try
{
dirIterator = Directory.EnumerateDirectories(path).GetEnumerator();
}
catch {}
if (dirIterator != null)
{
using (dirIterator)
{
while (true)
{
try
{
if (!dirIterator.MoveNext()) // Throws if directory is not accessible.
break;
}
catch { break; }
pending.Push(dirIterator.Current);
}
}
}
}
}
As an example, here's how you could use a console app to list all the accessible ".txt" files on the "C:\" drive:
static void Main()
{
foreach (var file in GetFiles("C:\\", "*.txt"))
{
Console.WriteLine(file);
}
}
Replace the lines
Console.WriteLine(file);
Console.ReadLine();
with a method to store them in a list.
For example
foundFiles.Add(file);
Then when the method is done, you can read all found file paths from this list.
Notes:
This will not yield all files on the system that match the filter.
Only files where your application has access to their respective directory are found this way.
For example the Windows directory and user directories of other users are usually protected. (assuming you run on Windows)
Keep in mind, that some files might be protected independently of their directory.
So when trying to read them, also consider the fact, that the read might fail.
Just encompass the read with a try catch.
Regarding the error "denied access to path (...)", sometimes you have to run Visual Studio as an a administrator in order to access some folders in the C:\ drive.
I have a program that a button, when clicked, executes a sound located in my download folder. My question is how to execute the sound on another computer if the path for finding it is different.
You need the path to a file to run it. If you don't have the path - you have to search for it.
Pick a base directory where you think the file is. If you don't know where - that will be the whole drive.
Write a recursive function that would search said folder recursively.
Test each file by what ever your search condition is, i.e. file name, file hash, etc.
For example:
string SearchForFile(string searchPath, Func<string, bool> searchPredicate)
{
try
{
foreach (string fileName in Directory.EnumerateFiles(searchPath))
{
if (searchPredicate(fileName))
{
return fileName;
}
}
foreach (string dirName in Directory.EnumerateDirectories(searchPath))
{
var childResult = SearchForFile(dirName, searchPredicate);
if (childResult != null)
{
return childResult;
}
}
return null;
}
catch (UnauthorizedAccessException)
{
return null;
}
}
Usage:
var filePath = SearchForFile(#"C:\", x => Path.GetFileName(x) == "yourFileName.mp3");
In my project ,I want to move a folder to a destination.Here is my thought.
First scenario, I want to check Can I move the folder,If I don't have the permission I will not check the sub-items in the folder. Move action is done.
second scenario,If I have the permisson to move the folder,I will check all the sub-items in the folder,then move the items that I can move and leave the items that I can't move.
So I don't know How to implement the first scenario.If I catch the unauthorizedaccessexception in the scenario.I may block the second scenario,because in the second scenario,while moving folder,it will also throw the exception if some sub-items can not be moved.Can someone give me some suggestions?
I did not test this but it should give you a start:
public static void MoveDirectory(string source, string target)
{
var delSourceAtEnd = true;
var sourcePath = source.TrimEnd('\\', ' ');
var targetPath = target.TrimEnd('\\', ' ');
var files = Directory.EnumerateFiles(sourcePath, "*", SearchOption.AllDirectories)
.GroupBy(Path.GetDirectoryName);
foreach (var folder in files)
{
var failed = false;
var targetFolder = folder.Key.Replace(sourcePath, targetPath);
Directory.CreateDirectory(targetFolder);
foreach (var file in folder)
{
var targetFile = Path.Combine(targetFolder, Path.GetFileName(file));
try
{
File.Move(file, targetFile);
} catch (UnauthorizedAccessException uae)
{
failed = true;
delSourceAtEnd = false;
}
}
if (!failed) Directory.Delete(folder.Key);
}
if (delSourceAtEnd) Directory.Delete(source, false);
}
This is heavily based on this answer, which shows different options how you can move a directory manually and handle single files and folders individually.
I am new to C# . I have a text box where i enter the file to search and a 'search' button. on clock of search i want it to populate the files in the folder but i get the above error. Below is my code:
string[] directories = Directory.GetDirectories(#"d:\",
"*",
SearchOption.AllDirectories);
string file = textBox1.Text;
DataGrid dg = new DataGrid();
{
var files = new List<string>();
foreach (DriveInfo d in DriveInfo.GetDrives().Where(x => x.IsReady))
{
try
{
files.AddRange(Directory.GetFiles(d.RootDirectory.FullName, file , SearchOption.AllDirectories));
}
catch(Exception ex)
{
MessageBox.Show("the exception is " + ex.ToString());
//Logger.Log(e.Message); // Log it and move on
}
}
Please help me resolve it . Thanks
The most important rule when searching on a folder which potentially contains inaccessible subfolder is:
Do NOT use SearchOption.AllDirectories!
Use SearchOption.TopDirectoryOnly instead, combined with recursive search for all the accessible directories.
Using SearchOption.AllDirectories, one access violation will break your entire loop even before any file/directory is processed. But if you use SearchOption.TopDirectoryOnly, you only skip what is inaccessible.
There is more difficult way to use Directory.GetAccessControl() per child directory check to see if you have an access to a Directory before hand (this option is rather hard though - I don't really recommend this unless you know exactly how the access system works).
For recursive search, I have this code implemented for my own use:
public static List<string> GetAllAccessibleDirectories(string path, string searchPattern) {
List<string> dirPathList = new List<string>();
try {
List<string> childDirPathList = Directory.GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly).ToList(); //use TopDirectoryOnly
if (childDirPathList == null || childDirPathList.Count <= 0) //this directory has no child
return null;
foreach (string childDirPath in childDirPathList) { //foreach child directory, do recursive search
dirPathList.Add(childDirPath); //add the path
List<string> grandChildDirPath = GetAllAccessibleDirectories(childDirPath, searchPattern);
if (grandChildDirPath != null && grandChildDirPath.Count > 0) //this child directory has children and nothing has gone wrong
dirPathList.AddRange(grandChildDirPath.ToArray()); //add the grandchildren to the list
}
return dirPathList; //return the whole list found at this level
} catch {
return null; //something has gone wrong, return null
}
}
This is how you call it
List<string> accessibleDirs = GetAllAccessibleDirectories(myrootpath, "*");
Then, you only need to search/add the files among all accessible directories.
Note: this question is quite classical though. I believe there are some other better solutions out there too.
And in case there are some directories which you particularly want to avoid after you get all your accessible directories, you could also filter the List result by LINQ using part of the directory's name as keyword (i.e. Recycle.Bins).
As Ian has specified in his post, do not use recursive file listing (Directory.GetFiles(path, searchPattern, SearchOption.AllDirectories)) in case like yours, since the first exception will stop further processing.
Also, to somewhat alleviate such issues and for better results in general, you should run this program as an Administrator. This can be done by right-clicking your application in windows explorer, and then checking Run this program as an administrator option on Compatibility tab.
Also, you should use code like below to do your search, so the intermediate exceptions do not stop further searching.
static void Main(string[] args) {
string fileToFind = "*.jpg";
var files = new List<string>();
foreach (DriveInfo d in DriveInfo.GetDrives().Where(x => x.IsReady))
files.AddRange(FindDirectory(fileToFind, d.RootDirectory.FullName));
}
/// <summary>
/// This function returns the full file path of the matches it finds.
/// 1. It does not do any parameter validation
/// 2. It searches recursively
/// 3. It eats up any error that occurs when requesting files and directories within the specified path
/// 4. Supports specifying wildcards in the fileToFind parameter.
/// </summary>
/// <param name="fileToFind">Name of the file to search, without the path</param>
/// <param name="path">The path under which the file needs to be searched</param>
/// <returns>Enumeration of all valid full file paths matching the file</returns>
public static IEnumerable<string> FindDirectory(string fileToFind, string path) {
// Check if "path" directly contains "fileToFind"
string[] files = null;
try {
files = Directory.GetFiles(path, fileToFind);
} catch { }
if (files != null) {
foreach (var file in files)
yield return file;
}
// Check all sub-directories of "path" to see if they contain "fileToFInd"
string[] subDirs = null;
try {
subDirs = Directory.GetDirectories(path);
} catch { }
if (subDirs == null)
yield break;
foreach (var subDir in subDirs)
foreach (var foundFile in FindDirectory(fileToFind, subDir))
yield return foundFile;
}
I have strange problem.
I have some folders in my Google Drive, and one is named myFolder. That folder has visibility option set to Anyone with the link.
I want to get that folder ID. To do so, I have the following code
ChildrenResource.ListRequest request = Google_DriveService.Children.List("root");
request.Q = "mimeType='application/vnd.google-apps.folder' and title='myFolder' and trashed=false";
try
{
ChildList children = request.Fetch();
return children.Items;
}
catch (Exception e)
{
throw e;
}
which throws error 500. But, if I change the query to "mimeType='application/vnd.google-apps.folder' and trashed=false"; I get the list of the folders, but there is no posibility to know which folder in that list is the one that I want.
So
1) why I get that error 500 if I pass the folder name inside that query ?
2) How to get folder details (e.g. its name) if I have ID ?
EDIT
OK, I've changed the code as you guys suggested, now I don't get the error, but the folder is still not found. It is placed in the root folder.
var request = Google_DriveService.Files.List();
request.Q = "mimeType='application/vnd.google-apps.folder' and title='myFolder' and trashed=false";
var children = request.Fetch();
return children.Items.Count > 0 ? children.Items.First().Id : null;
1 . Probably a Bug. Try running your query from https://developers.google.com/drive/v2/reference/files/list#try-it to confirm
What scope are you using? If drive.file, try switching to drive
2 . In the returned JSON, look for 'title'. If you need to be sure it's the right file, you maybe need to check the parent id vs the parent id of myfolder.
I think you need to remove the root you have in there. This works. Not sure which client api you are using. This is using the latest.
FilesResource.ListRequest request = service.Files.List();
request.MaxResults = 1000;
request.Q = "mimeType='application/vnd.google-apps.folder' and trashed=false and title='myFolder'";
Here is a code snippet uses Google.Apis.Drice.V2 Client Library that can be used for locate a folder in Google Drive.
using Google.Apis.Drive.v2;
using Google.Apis.Drive.v2.Data;
using Google.Apis.Discovery;
using Google.Apis.Upload;
string search = string.Format("title = '{0}' and mimeType =
'application/vnd.google-apps.folder'",folderName);
IList<File> files = new List<File>();
try
{
//List all of the files and directories for the current user.
// Documentation: https://developers.google.com/drive/v2/reference/files/list
FilesResource.ListRequest list = _service.Files.List();
list.MaxResults = 1000;
if (search != null)
{
list.Q = search;
}
FileList filesFeed = list.Execute();
//// Loop through until we arrive at an empty page
while (filesFeed.Items != null)
{
// Adding each item to the list.
foreach (File item in filesFeed.Items)
{
files.Add(item);
}
// We will know we are on the last page when the next page token is
// null.
// If this is the case, break.
if (filesFeed.NextPageToken == null)
{
break;
}
// Prepare the next page of results
list.PageToken = filesFeed.NextPageToken;
// Execute and process the next page request
filesFeed = list.Execute();
}
}
catch (Exception ex)
{
// In the event there is an error with the request.
Console.WriteLine(ex.Message);
}