c# - obtener - Obtenga todos los archivos y directorios en una ruta específica rápida
sacar el nombre de un archivo c# (7)
Estoy creando una aplicación de respaldo donde c # escanea un directorio. Antes solía tener algo como esto para obtener todos los archivos y subarchivos en un directorio:
DirectoryInfo di = new DirectoryInfo("A://");
var directories= di.GetFiles("*", SearchOption.AllDirectories);
foreach (FileInfo d in directories)
{
//Add files to a list so that later they can be compared to see if each file
// needs to be copid or not
}
El único problema con eso es que a veces no se puede acceder a un archivo y recibo varios errores. un ejemplo de un error que obtengo es:
Como resultado, creé un método recursivo que escaneará todos los archivos en el directorio actual. Si hay directorios en ese directorio, entonces el método se llamará de nuevo pasando ese directorio. Lo bueno de este método es que podría colocar los archivos dentro de un bloque try catch dándome la opción de agregar esos archivos a una lista si no hubiera errores y agregar el directorio a otra lista si tuviera errores.
try
{
files = di.GetFiles(searchPattern, SearchOption.TopDirectoryOnly);
}
catch
{
//info of this folder was not able to get
lstFilesErrors.Add(sDir(di));
return;
}
Así que este método funciona muy bien, el único problema es que cuando escaneo un directorio grande lo lleva a muchas veces. ¿Cómo podría acelerar este proceso? Mi método real es esto en caso de que lo necesites.
private void startScan(DirectoryInfo di)
{
//lstFilesErrors is a list of MyFile objects
// I created that class because I wanted to store more specific information
// about a file such as its comparePath name and other properties that I need
// in order to compare it with another list
// lstFiles is a list of MyFile objects that store all the files
// that are contained in path that I want to scan
FileInfo[] files = null;
DirectoryInfo[] directories = null;
string searchPattern = "*.*";
try
{
files = di.GetFiles(searchPattern, SearchOption.TopDirectoryOnly);
}
catch
{
//info of this folder was not able to get
lstFilesErrors.Add(sDir(di));
return;
}
// if there are files in the directory then add those files to the list
if (files != null)
{
foreach (FileInfo f in files)
{
lstFiles.Add(sFile(f));
}
}
try
{
directories = di.GetDirectories(searchPattern, SearchOption.TopDirectoryOnly);
}
catch
{
lstFilesErrors.Add(sDir(di));
return;
}
// if that directory has more directories then add them to the list then
// execute this function
if (directories != null)
foreach (DirectoryInfo d in directories)
{
FileInfo[] subFiles = null;
DirectoryInfo[] subDir = null;
bool isThereAnError = false;
try
{
subFiles = d.GetFiles();
subDir = d.GetDirectories();
}
catch
{
isThereAnError = true;
}
if (isThereAnError)
lstFilesErrors.Add(sDir(d));
else
{
lstFiles.Add(sDir(d));
startScan(d);
}
}
}
Ant el problema si trato de manejar la excepción con algo como:
DirectoryInfo di = new DirectoryInfo("A://");
FileInfo[] directories = null;
try
{
directories = di.GetFiles("*", SearchOption.AllDirectories);
}
catch (UnauthorizedAccessException e)
{
Console.WriteLine("There was an error with UnauthorizedAccessException");
}
catch
{
Console.WriteLine("There was antother error");
}
Es que si ocurre una excepción, entonces no obtengo ningún archivo.
En .NET 4.0 está el método Directory.EnumerateFiles que devuelve una IEnumerable<string>
y no está cargando todos los archivos en la memoria. Es solo cuando comienza a iterar sobre la colección devuelta que se devolverán los archivos y se podrán manejar las excepciones .
Este método es mucho más rápido. Solo puede marcar cuando coloca muchos archivos en un directorio. Mi disco duro A: / externo contiene casi 1 terabit, por lo que hace una gran diferencia cuando se trata de una gran cantidad de archivos.
static void Main(string[] args)
{
DirectoryInfo di = new DirectoryInfo("A://");
FullDirList(di, "*");
Console.WriteLine("Done");
Console.Read();
}
static List<FileInfo> files = new List<FileInfo>(); // List that will hold the files and subfiles in path
static List<DirectoryInfo> folders = new List<DirectoryInfo>(); // List that hold direcotries that cannot be accessed
static void FullDirList(DirectoryInfo dir, string searchPattern)
{
// Console.WriteLine("Directory {0}", dir.FullName);
// list the files
try
{
foreach (FileInfo f in dir.GetFiles(searchPattern))
{
//Console.WriteLine("File {0}", f.FullName);
files.Add(f);
}
}
catch
{
Console.WriteLine("Directory {0} /n could not be accessed!!!!", dir.FullName);
return; // We alredy got an error trying to access dir so dont try to access it again
}
// process each directory
// If I have been able to see the files in the directory I should also be able
// to look at its directories so I dont think I should place this in a try catch block
foreach (DirectoryInfo d in dir.GetDirectories())
{
folders.Add(d);
FullDirList(d, searchPattern);
}
}
Por cierto, lo recibí gracias a tu comentario Jim Mischel
Existe una larga historia de que los métodos de enumeración de archivos .NET son lentos. El problema es que no hay una forma instantánea de enumerar estructuras de directorios grandes. Incluso la respuesta aceptada aquí tiene sus problemas con las asignaciones de GC.
Lo mejor que he podido hacer está envuelto en mi biblioteca y expuesto como la FindFile ( source ) en el espacio de nombres CSharpTest.Net.IO . Esta clase puede enumerar archivos y carpetas sin asignaciones GC innecesarias y clasificación de cadenas.
El uso es lo suficientemente simple, y la propiedad RaiseOnAccessDenied omitirá los directorios y archivos a los que el usuario no tiene acceso:
private static long SizeOf(string directory)
{
var fcounter = new CSharpTest.Net.IO.FindFile(directory, "*", true, true, true);
fcounter.RaiseOnAccessDenied = false;
long size = 0, total = 0;
fcounter.FileFound +=
(o, e) =>
{
if (!e.IsDirectory)
{
Interlocked.Increment(ref total);
size += e.Length;
}
};
Stopwatch sw = Stopwatch.StartNew();
fcounter.Find();
Console.WriteLine("Enumerated {0:n0} files totaling {1:n0} bytes in {2:n3} seconds.",
total, size, sw.Elapsed.TotalSeconds);
return size;
}
Para mi unidad local C: / esto produce lo siguiente:
Enumeraron 810.046 archivos por un total de 307.707.792.662 bytes en 232.876 segundos.
Su kilometraje puede variar según la velocidad de la unidad, pero este es el método más rápido que he encontrado para enumerar archivos en código administrado. El parámetro de evento es una clase mutante de tipo source así que asegúrese de no mantener una referencia ya que los valores cambiarán para cada evento que se genere.
Puede usar esto para obtener todos los directorios y subdirectorios. Luego simplemente realiza un bucle para procesar los archivos.
string[] folders = System.IO.Directory.GetDirectories(@"C:/My Sample Path/","*", System.IO.SearchOption.AllDirectories);
foreach(string f in folders)
{
//call some function to get all files in folder
}
Sé que esto es viejo, pero ... Otra opción puede ser usar FileSystemWatcher de la siguiente manera:
void SomeMethod()
{
System.IO.FileSystemWatcher m_Watcher = new System.IO.FileSystemWatcher();
m_Watcher.Path = path;
m_Watcher.Filter = "*.*";
m_Watcher.NotifyFilter = m_Watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
m_Watcher.Created += new FileSystemEventHandler(OnChanged);
m_Watcher.EnableRaisingEvents = true;
}
private void OnChanged(object sender, FileSystemEventArgs e)
{
string path = e.FullPath;
lock (listLock)
{
pathsToUpload.Add(path);
}
}
Esto le permitiría ver los directorios de cambios de archivos con un proceso extremadamente liviano, que luego podría usar para almacenar los nombres de los archivos que cambiaron para que pueda realizar una copia de seguridad en el momento apropiado.
Tal vez sea útil para ti. Puede usar el método " DirectoryInfo.EnumerateFiles " y manejar la excepción de acceso no autorizado que necesite.
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
DirectoryInfo diTop = new DirectoryInfo(@"d:/");
try
{
foreach (var fi in diTop.EnumerateFiles())
{
try
{
// Display each file over 10 MB;
if (fi.Length > 10000000)
{
Console.WriteLine("{0}/t/t{1}", fi.FullName, fi.Length.ToString("N0"));
}
}
catch (UnauthorizedAccessException UnAuthTop)
{
Console.WriteLine("{0}", UnAuthTop.Message);
}
}
foreach (var di in diTop.EnumerateDirectories("*"))
{
try
{
foreach (var fi in di.EnumerateFiles("*", SearchOption.AllDirectories))
{
try
{
// Display each file over 10 MB;
if (fi.Length > 10000000)
{
Console.WriteLine("{0}/t/t{1}", fi.FullName, fi.Length.ToString("N0"));
}
}
catch (UnauthorizedAccessException UnAuthFile)
{
Console.WriteLine("UnAuthFile: {0}", UnAuthFile.Message);
}
}
}
catch (UnauthorizedAccessException UnAuthSubDir)
{
Console.WriteLine("UnAuthSubDir: {0}", UnAuthSubDir.Message);
}
}
}
catch (DirectoryNotFoundException DirNotFound)
{
Console.WriteLine("{0}", DirNotFound.Message);
}
catch (UnauthorizedAccessException UnAuthDir)
{
Console.WriteLine("UnAuthDir: {0}", UnAuthDir.Message);
}
catch (PathTooLongException LongPath)
{
Console.WriteLine("{0}", LongPath.Message);
}
}
}
(copia esta pieza de mi otra respuesta en tu otra pregunta)
Mostrar progreso al buscar todos los archivos en un directorio
Enumeración de archivos rápidos
Por supuesto, como ya sabes, hay muchas maneras de hacer la enumeración en sí ... pero ninguna será instantánea. Podría intentar usar el USN Journal del sistema de archivos para hacer el escaneo. Eche un vistazo a este proyecto en CodePlex: MFT Scanner en VB.NET ... encontró todos los archivos en mi unidad IDE SATA (no SSD) en menos de 15 segundos, y encontró 311000 archivos.
Tendrá que filtrar los archivos por ruta, de modo que solo se devuelvan los archivos dentro de la ruta que está buscando. ¡Pero esa es la parte fácil del trabajo!