net - obtener archivos de directorio c#
Error de acceso a archivos con FileSystemWatcher cuando se agregan varios archivos a un directorio (9)
Cuando abre el archivo en su método OnChanged, está especificando FileShare.None
, que de acuerdo con la documentación , hará que cualquier otro intento de abrir el archivo falle mientras lo tiene abierto. Como todo lo que usted (y su observador) están haciendo es leer, intente utilizar FileShare.Read
. FileShare.Read
en FileShare.Read
lugar.
Me encuentro con un problema con un FileSystemWatcher cuando se colocan varios archivos en el directorio mirado. Quiero analizar el archivo tan pronto como se coloca en el directorio. Normalmente, el primer archivo analiza bien, pero agregar un segundo archivo al directorio provoca un problema de acceso. Ocasionalmente, el primer archivo ni siquiera analiza. Solo hay una aplicación ejecutándose y mirando este directorio. Eventualmente, este proceso se ejecutará en varias máquinas y verán un directorio compartido, pero solo un servidor puede analizar cada archivo a medida que los datos se importan en una base de datos y no hay claves principales.
Aquí está el código FileSystemWatcher:
public void Run() {
FileSystemWatcher watcher = new FileSystemWatcher("C://temp");
watcher.NotifyFilter = NotifyFilters.FileName;
watcher.Filter = "*.txt";
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
}
Luego, el método que analiza el archivo:
private void OnChanged(object source, FileSystemEventArgs e) {
string line = null;
try {
using (FileStream fs = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.None)) {
using (StreamReader sr = new StreamReader(fs)) {
while (sr.EndOfStream == false) {
line = sr.ReadLine();
//parse the line and insert into the database
}
}
}
}
catch (IOException ioe) {
Console.WriteLine("OnChanged: Caught Exception reading file [{0}]", ioe.ToString());
}
Al mover el segundo archivo, está atrapando
System.IO.IOException: el proceso no puede acceder al archivo ''C: / Temp / TestFile.txt'' porque lo está utilizando otro proceso.
Esperaría ver este error si se ejecuta en varias máquinas, pero solo se está ejecutando en un servidor por ahora. No debería haber otro proceso que use este archivo: los tengo creados y los copio en el directorio cuando se está ejecutando la aplicación.
¿Es esta la manera correcta de configurar FileSystemWatcher? ¿Cómo puedo ver qué tiene el bloqueo en este archivo? ¿Por qué no analiza ambos archivos? ¿Tengo que cerrar FileStream? Quiero mantener la opción FileShare.None porque solo quiero que un servidor analice el archivo: el servidor que accede al archivo primero lo analiza.
FileSystemWatcher activa el evento watcher.Created dos veces para cada creación de archivo 1ce cuando se inicia la copia de archivo y la segunda vez cuando finaliza la copia de archivo. Todo lo que tienes que hacer es ignorar el primer evento y el evento del proceso la segunda vez.
Un ejemplo simple de controlador de eventos:
private bool _fileCreated = false;
private void FileSystemWatcher_FileCreated(object sender, FileSystemEventArgs e)
{
if (_fileCreated)
{
ReadFromFile();//just an example method call to access the new file
}
_fileCreated = !_fileCreated;
}
Hubiera dejado un comentario arriba, pero todavía no tengo suficientes puntos.
La respuesta mejor calificada a esta pregunta tiene un bloque de código que se ve así:
using (Stream stream = System.IO.File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
if (stream != null)
{
System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} ready.", fileName));
break;
}
}
El problema con el uso de la configuración FileShare.ReadWrite
es que solicita acceso al archivo básicamente diciendo "Quiero leer / escribir en este archivo, pero otros también pueden leer / escribir en él". Este enfoque falló en nuestra situación. El proceso que recibía la transferencia remota no bloqueaba el archivo, pero estaba escribiéndole activamente. Nuestro código de flujo descendente (SharpZipLib) estaba fallando con la excepción de "archivo en uso" porque intentaba abrir el archivo con FileShare.Read
("Quiero que el archivo sea leído y solo dejo que otros procesos también lo lean"). Como el proceso que tenía el archivo abierto ya estaba escrito en él, esta solicitud falló.
Sin embargo, el código en la respuesta anterior es demasiado relajado. Al utilizar FileShare.ReadWrite
, FileShare.ReadWrite
obtener acceso al archivo (porque pedía una restricción de Compartir que podía cumplirse), pero la llamada en sentido descendente continuó fallando.
La configuración de compartir en la llamada a File.Open
debe ser FileShare.Read
o FileShare.None
, y NO FileShare.ReadWrite
.
Siento que un buen ejemplo de lo que desea es ConfigureAndWatchHandler en log4net. Usan un temporizador para disparar el evento del manejador de archivos. Siento que esto termina siendo una implementación más limpia del ciclo while en la publicación de 0xA3. Para aquellos de ustedes que no quieran usar dotPeek para examinar el archivo, intentaré proporcionarles aquí un fragmento de código basado en el código OP:
private System.Threading.Timer _timer;
public void Run() {
//setup filewatcher
_timer = new System.Threading.Timer(new TimerCallback(OnFileChange), (object) null, -1, -1);
}
private void OnFileChange(object state)
{
try
{
//handle files
}
catch (Exception ex)
{
//log exception
_timer.Change(500, -1);
}
}
Tuve el mismo problema dentro de DFS. Mi resolución fue alcanzada agregando dos líneas vacías a cada archivo. Entonces mi código espera dos líneas vacías en el archivo. Entonces tengo la certeza de leer datos enteros del archivo.
Tuve un problema similar. Es solo por FileSystemWatcher. Acabo de usar
Thread.Sleep ();
Y está funcionando bien ahora. Cuando el archivo viene en el directorio, llama a Creado dos veces. así que una vez cuando el archivo se copia. y la segunda vez cuando se completa la copia. Para eso usé Thread.Sleep (); Entonces esperará antes de llamar a ReadFile ();
private static void OnCreated(object source, FileSystemEventArgs e)
{
try
{
Thread.Sleep(5000);
var data = new FileData();
data.ReadFile(e.FullPath);
}
catch (Exception ex)
{
WriteLogforError(ex.Message, String.Empty, filepath);
}
}
Un problema típico de este enfoque es que el archivo aún se está copiando mientras se desencadena el evento. Obviamente, obtendrá una excepción porque el archivo está bloqueado durante la copia. Una excepción es especialmente probable en archivos grandes.
Como solución alternativa, primero puede copiar el archivo y luego cambiarle el nombre y escuchar el evento de cambio de nombre.
Otra opción sería tener un ciclo while para verificar si el archivo se puede abrir con acceso de escritura. Si puede, sabrá que se ha completado la copia. El código C # podría verse así (en un sistema de producción, es posible que desee tener un número máximo de reintentos o tiempo de espera en lugar de un while(true)
):
/// <summary>
/// Waits until a file can be opened with write permission
/// </summary>
public static void WaitReady(string fileName)
{
while (true)
{
try
{
using (Stream stream = System.IO.File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
if (stream != null)
{
System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} ready.", fileName));
break;
}
}
}
catch (FileNotFoundException ex)
{
System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message));
}
catch (IOException ex)
{
System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message));
}
catch (UnauthorizedAccessException ex)
{
System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message));
}
Thread.Sleep(500);
}
}
Sin embargo, otro enfoque sería colocar un pequeño archivo de activación en la carpeta una vez que se haya completado la copia. Su FileSystemWatcher solo escuchará el archivo desencadenante.
Una solución simple sería eliminar el sistema de archivos vigilante una vez que reciba la notificación. antes de copiar el archivo, haga que el hilo actual espere hasta que reciba el evento dispuesto por el sistema de archivos. entonces puedes continuar copiando el archivo modificado sin problemas de acceso. Tenía el mismo requisito y lo hice exactamente como lo mencioné. funcionó.
Código de ejemplo:
public void TestWatcher()
{
using (var fileWatcher = new FileSystemWatcher())
{
string path = @"C:/sv";
string file = "pos.csv";
fileWatcher.Path = path;
fileWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite;
fileWatcher.Filter = file;
System.EventHandler onDisposed = (sender,args) =>
{
eve.Set();
};
FileSystemEventHandler onFile = (sender, fileChange) =>
{
fileWatcher.EnableRaisingEvents = false;
Thread t = new Thread(new ParameterizedThreadStart(CopyFile));
t.Start(fileChange.FullPath);
if (fileWatcher != null)
{
fileWatcher.Dispose();
}
proceed = false;
};
fileWatcher.Changed += onFile;
fileWatcher.Created += onFile;
fileWatcher.Disposed+= onDisposed;
fileWatcher.EnableRaisingEvents = true;
while (proceed)
{
if (!proceed)
{
break;
}
}
}
}
public void CopyFile(object sourcePath)
{
eve.WaitOne();
var destinationFilePath = @"C:/sv/Co";
if (!string.IsNullOrEmpty(destinationFilePath))
{
if (!Directory.Exists(destinationFilePath))
{
Directory.CreateDirectory(destinationFilePath);
}
destinationFilePath = Path.Combine(destinationFilePath, "pos.csv");
}
File.Copy((string)sourcePath, destinationFilePath);
}
public static BitmapSource LoadImageNoLock(string path)
{
while (true)
{
try
{
var memStream = new MemoryStream(File.ReadAllBytes(path));
var img = new BitmapImage();
img.BeginInit();
img.StreamSource = memStream;
img.EndInit();
return img;
break;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}