Voici une solution qui peut être excessive pour certains utilisateurs. J'ai créé une nouvelle classe statique qui a un événement qui n'est déclenché que lorsque le fichier a fini de copier. L'utilisateur enregistre les fichiers qu'il souhaite regarder en appelant le FileAccessWatcher.RegisterWaitForFileAccess(filePath)
. Si le fichier n'est pas déjà regardé, une nouvelle tâche est démarrée qui vérifie à plusieurs reprises le fichier pour voir s'il peut être ouvert. Chaque fois qu'il vérifie, il lit également la taille du fichier. Si la taille du fichier n'augmente pas dans un temps prédéfini (5 minutes dans mon exemple), la boucle est fermée.
Lorsque la boucle quitte le fichier accessible ou à partir du délai d'attente, l'événement FileFinishedCopying
est déclenché.
public class FileAccessWatcher
{
// this list keeps track of files being watched
private static ConcurrentDictionary<string, FileAccessWatcher> watchedFiles = new ConcurrentDictionary<string, FileAccessWatcher>();
public static void RegisterWaitForFileAccess(string filePath)
{
// if the file is already being watched, don't do anything
if (watchedFiles.ContainsKey(filePath))
{
return;
}
// otherwise, start watching it
FileAccessWatcher accessWatcher = new FileAccessWatcher(filePath);
watchedFiles[filePath] = accessWatcher;
accessWatcher.StartWatching();
}
/// <summary>
/// Event triggered when the file is finished copying or when the file size has not increased in the last 5 minutes.
/// </summary>
public static event FileSystemEventHandler FileFinishedCopying;
private static readonly TimeSpan MaximumIdleTime = TimeSpan.FromMinutes(5);
private readonly FileInfo file;
private long lastFileSize = 0;
private DateTime timeOfLastFileSizeIncrease = DateTime.Now;
private FileAccessWatcher(string filePath)
{
this.file = new FileInfo(filePath);
}
private Task StartWatching()
{
return Task.Factory.StartNew(this.RunLoop);
}
private void RunLoop()
{
while (this.IsFileLocked())
{
long currentFileSize = this.GetFileSize();
if (currentFileSize > this.lastFileSize)
{
this.lastFileSize = currentFileSize;
this.timeOfLastFileSizeIncrease = DateTime.Now;
}
// if the file size has not increased for a pre-defined time limit, cancel
if (DateTime.Now - this.timeOfLastFileSizeIncrease > MaximumIdleTime)
{
break;
}
}
this.RemoveFromWatchedFiles();
this.RaiseFileFinishedCopyingEvent();
}
private void RemoveFromWatchedFiles()
{
FileAccessWatcher accessWatcher;
watchedFiles.TryRemove(this.file.FullName, out accessWatcher);
}
private void RaiseFileFinishedCopyingEvent()
{
FileFinishedCopying?.Invoke(this,
new FileSystemEventArgs(WatcherChangeTypes.Changed, this.file.FullName, this.file.Name));
}
private long GetFileSize()
{
return this.file.Length;
}
private bool IsFileLocked()
{
try
{
using (this.file.Open(FileMode.Open)) { }
}
catch (IOException e)
{
var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
return errorCode == 32 || errorCode == 33;
}
return false;
}
}
Exemple d'utilisation:
// register the event
FileAccessWatcher.FileFinishedCopying += FileAccessWatcher_FileFinishedCopying;
// start monitoring the file (put this inside the OnChanged event handler of the FileSystemWatcher
FileAccessWatcher.RegisterWaitForFileAccess(fileSystemEventArgs.FullPath);
Manipulez le FileFinishedCopyingEvent:
private void FileAccessWatcher_FileFinishedCopying(object sender, FileSystemEventArgs e)
{
Console.WriteLine("File finished copying: " + e.FullPath);
}
double possible de [Existe-t-il un moyen de vérifier si un fichier est en cours d'utilisation?] (http://stackoverflow.com/questions/876473/is-there-a-way-to-check-if-a-file-is-in -use) –
Ce code a un bug simple avec 'File.Create (fileName)'. Les réponses manquent ce point. Il n'est pas nécessaire d'attendre la fermeture. – usr