c# - UnauthorizedAccessException no puede resolver Directory.GetFiles failure
exception-handling (7)
. Directory.EnumerateFiles de .NET 4 funciona, pero debe tener cuidado al evaluar el enumerable y hacer esa parte dentro del bloque try-catch. El mayor problema es asegurarse de no detener el procesamiento en la primera excepción (que creo que la respuesta https://stackoverflow.com/a/1393219/89584 tiene este problema, corrígeme si me equivoco).
Lo siguiente funciona y te proporciona un Enumerable para que no tengas que evaluar todo el árbol de archivos si estás buscando la primera coincidencia, etc.
private IEnumerable<String> FindAccessableFiles(string path, string file_pattern, bool recurse)
{
IEnumerable<String> emptyList = new string[0];
if (File.Exists(path))
return new string[] { path };
if (!Directory.Exists(path))
return emptyList;
var top_directory = new DirectoryInfo(path);
// Enumerate the files just in the top directory.
var files = top_directory.EnumerateFiles(file_pattern);
var filesLength = files.Count();
var filesList = Enumerable
.Range(0, filesLength)
.Select(i =>
{
string filename = null;
try
{
var file = files.ElementAt(i);
filename = file.FullName;
}
catch (UnauthorizedAccessException)
{
}
catch (InvalidOperationException)
{
// ran out of entries
}
return filename;
})
.Where(i => null != i);
if (!recurse)
return filesList;
var dirs = top_directory.EnumerateDirectories("*");
var dirsLength = dirs.Count();
var dirsList = Enumerable
.Range(0, dirsLength)
.SelectMany(i =>
{
string dirname = null;
try
{
var dir = dirs.ElementAt(i);
dirname = dir.FullName;
return FindAccessableFiles(dirname, file_pattern, required_extension, recurse);
}
catch (UnauthorizedAccessException)
{
}
catch (InvalidOperationException)
{
// ran out of entries
}
return emptyList;
})
return Enumerable.Concat(filesList, dirsList);
}
mejoras a la bienvenida anterior.
Esta pregunta ya tiene una respuesta aquí:
El método Directory.GetFiles falla en el primer encuentro con una carpeta a la que no tiene derechos de acceso.
El método arroja una excepción de acceso no autorizada (que se puede capturar), pero cuando se hace esto, el método ya ha fallado / terminado.
El código que estoy usando se detalla a continuación:
try
{
// looks in stated directory and returns the path of all files found
getFiles = Directory.GetFiles(
@directoryToSearch,
filetype,
SearchOption.AllDirectories);
}
catch (UnauthorizedAccessException)
{
}
Hasta donde yo sé, no hay forma de verificar de antemano si una determinada carpeta tiene derechos de acceso definidos.
En mi ejemplo, estoy buscando en un disco en una red y cuando encuentro una carpeta de acceso raíz, mi programa falla.
En .NET 4, esto se vuelve mucho más fácil, consulte http://msdn.microsoft.com/en-us/library/dd997370.aspx
Esta es una mejora de la respuesta de Malcolm (http://.com/a/9831340/226181). Esto escanea todas las unidades lógicas para un patrón de coincidencia de archivos e ignora los directorios que no son accesibles.
static List<string> SearchFiles(string pattern)
{
var result = new List<string>();
foreach (string drive in Directory.GetLogicalDrives())
{
Console.WriteLine("searching " + drive);
var files = FindAccessableFiles(drive, pattern, true);
Console.WriteLine(files.Count().ToString() + " files found.");
result.AddRange(files);
}
return result;
}
private static IEnumerable<String> FindAccessableFiles(string path, string file_pattern, bool recurse)
{
Console.WriteLine(path);
var list = new List<string>();
var required_extension = "mp4";
if (File.Exists(path))
{
yield return path;
yield break;
}
if (!Directory.Exists(path))
{
yield break;
}
if (null == file_pattern)
file_pattern = "*." + required_extension;
var top_directory = new DirectoryInfo(path);
// Enumerate the files just in the top directory.
IEnumerator<FileInfo> files;
try
{
files = top_directory.EnumerateFiles(file_pattern).GetEnumerator();
}
catch (Exception ex)
{
files = null;
}
while (true)
{
FileInfo file = null;
try
{
if (files != null && files.MoveNext())
file = files.Current;
else
break;
}
catch (UnauthorizedAccessException)
{
continue;
}
catch (PathTooLongException)
{
continue;
}
yield return file.FullName;
}
if (!recurse)
yield break;
IEnumerator<DirectoryInfo> dirs;
try
{
dirs = top_directory.EnumerateDirectories("*").GetEnumerator();
}
catch (Exception ex)
{
dirs = null;
}
while (true)
{
DirectoryInfo dir = null;
try
{
if (dirs != null && dirs.MoveNext())
dir = dirs.Current;
else
break;
}
catch (UnauthorizedAccessException)
{
continue;
}
catch (PathTooLongException)
{
continue;
}
foreach (var subpath in FindAccessableFiles(dir.FullName, file_pattern, recurse))
yield return subpath;
}
}
La versión más simple:
IEnumerable<String> GetAllFiles(string path, string searchPattern)
{
return System.IO.Directory.EnumerateFiles(path, searchPattern).Union(
System.IO.Directory.EnumerateDirectories(path).SelectMany(d =>
{
try
{
return GetAllFiles(d, searchPattern);
}
catch (UnauthorizedAccessException e)
{
return Enumerable.Empty<String>();
}
}));
}
Para obtener el control en el nivel que desea, probablemente debería sondear un directorio a la vez, en lugar de un árbol completo. El siguiente método rellena el IList<string>
dado con todos los archivos encontrados en el árbol del directorio, excepto aquellos a los que el usuario no tiene acceso:
// using System.Linq
private static void AddFiles(string path, IList<string> files)
{
try
{
Directory.GetFiles(path)
.ToList()
.ForEach(s => files.Add(s));
Directory.GetDirectories(path)
.ToList()
.ForEach(s => AddFiles(s, files));
}
catch (UnauthorizedAccessException ex)
{
// ok, so we are not allowed to dig into that directory. Move on.
}
}
Sé que este hilo es viejo, pero en caso de que alguien se tropiece con esto y necesite una respuesta, tengo una solución recursiva aquí:
public static List<string> GetAllAccessibleFiles(string rootPath, List<string> alreadyFound = null)
{
if (alreadyFound == null)
alreadyFound = new List<string>();
DirectoryInfo di = new DirectoryInfo(rootPath);
var dirs = di.EnumerateDirectories();
foreach (DirectoryInfo dir in dirs)
{
if (!((dir.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden))
{
alreadyFound = GetAllAccessibleFiles(dir.FullName, alreadyFound);
}
}
var files = Directory.GetFiles(rootPath);
foreach (string s in files)
{
alreadyFound.Add(s);
}
return alreadyFound;
}
Devuelve una List<string>
contiene la ruta completa a todos los archivos que están en directorios accesibles debajo del directorio raíz dado. Llámalo así:
var files = GetAllAccessibleFiles(@"C:/myDirectory");
Entonces, un resultado podría ser así:
C:/myDirectory/a/a.txt
C:/myDirectory/a/b.mp3
C:/myDirectory/b/a/a/foo.txt
C:/myDirectory/b/b/b/hello.exe
C:/myDirectory/b/c/bar.jpg
C:/myDirectory/and/so/on.bar
C:/myDirectory/a_file_in_root.bmp
Espero que ayude a alguien!
public string[] GetFilesFrom(string dir, string search_pattern, bool recursive)
{
List<string> files = new List<string>();
string[] temp_files = new string[0];
try { temp_files = Directory.GetFiles(dir, search_pattern, SearchOption.TopDirectoryOnly); }
catch { }
files.AddRange(temp_files);
if (recursive)
{
string[] temp_dirs = new string[0];
try { temp_dirs = Directory.GetDirectories(dir, search_pattern, SearchOption.TopDirectoryOnly); }
catch { }
for (int i = 0; i < temp_dirs.Length; i++)
files.AddRange(GetFilesFrom(temp_dirs[i], search_pattern, recursive));
}
return files.ToArray();
}
Esta es mi solución para este problema. Simple y a prueba de fallas.