Le partage de fichiers volumineux sur un réseau local (LAN) nécessite souvent un espace de stockage temporaire. Pour éviter l'accumulation de données obsolètes sans recourir à des logiciels tiers, il est possible de déployer un service Windows personnalisé. Ce service surveille les transferts entrants et nettoei automatiquement les fichiers expirés.
La configuration recommandée consiste à créer un dossier partagé sur le serveur avec des permissions restrictives pour les clients réseau (lecture et écriture uniquement, sans droit de suppression). Le service local, s'exécutant avec des privilèges appropriés, se chargera de la gestion du cycle de vie et de la suppression des fichiers.
Implémentation du Service de Surveillance
L'approche suivante utilise FileSystemWatcher pour détecter les nouveaux fichiers. Un dictionnaire thread-safe suit les transferts en cours pour éviter les suppressions prématurées. Une fois le transfert terminé (détecté par la stabilisation de la taille du fichier), le service met à jour l'horodatage. Une tâche d'arrière-plan périodique supprime ensuite les fichiers inactifs depuis plus de huit heures.
using System;
using System.Collections.Concurrent;
using System.IO;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
namespace NetworkFileMonitor
{
public class FileMonitorService : ServiceBase
{
private FileSystemWatcher _watcher;
private CancellationTokenSource _cancellationTokenSource;
private readonly ConcurrentDictionary<string, byte> _activeTransfers = new ConcurrentDictionary<string, byte>();
private readonly string _targetDirectory = @"C:\TempShare";
private readonly TimeSpan _expirationTime = TimeSpan.FromHours(8);
protected override void OnStart(string[] args)
{
_cancellationTokenSource = new CancellationTokenSource();
_watcher = new FileSystemWatcher(_targetDirectory)
{
NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite,
IncludeSubdirectories = false,
EnableRaisingEvents = true
};
_watcher.Created += OnFileCreated;
Task.Run(() => CleanupRoutineAsync(_cancellationTokenSource.Token));
}
protected override void OnStop()
{
_cancellationTokenSource?.Cancel();
if (_watcher != null)
{
_watcher.EnableRaisingEvents = false;
_watcher.Dispose();
}
}
private async void OnFileCreated(object sender, FileSystemEventArgs e)
{
if (_activeTransfers.TryAdd(e.Name, 0))
{
await WaitForTransferCompletionAsync(e.FullPath);
_activeTransfers.TryRemove(e.Name, out _);
// Mettre à jour l'horodatage pour indiquer la fin du transfert
try { File.SetLastWriteTimeUtc(e.FullPath, DateTime.UtcNow); } catch { }
}
}
private async Task WaitForTransferCompletionAsync(string filePath)
{
long previousLength = -1;
long currentLength = 0;
while (previousLength != currentLength)
{
previousLength = currentLength;
await Task.Delay(1000); // Pause pour permettre l'écriture
try
{
var fileInfo = new FileInfo(filePath);
fileInfo.Refresh();
currentLength = fileInfo.Exists ? fileInfo.Length : 0;
}
catch (IOException)
{
// Le fichier est encore verrouillé par le processus d'écriture
currentLength = -1;
}
}
}
private async Task CleanupRoutineAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
var files = Directory.GetFiles(_targetDirectory);
foreach (var file in files)
{
var fileName = Path.GetFileName(file);
if (fileName.Equals("readme.txt", StringComparison.OrdinalIgnoreCase)) continue;
if (_activeTransfers.ContainsKey(fileName)) continue;
var fileInfo = new FileInfo(file);
if (DateTime.UtcNow - fileInfo.LastWriteTimeUtc > _expirationTime)
{
try { fileInfo.Delete(); } catch { }
}
}
}
catch { /* Une journalisation devrait être implémentée ici */ }
await Task.Delay(TimeSpan.FromMinutes(10), token);
}
}
}
}
Point d'Entrée de l'Application
Le point d'entrée standard pour un service Windows permet l'initialisation du composant. En mode débogage, il est utile d'inclure un mécanisme pour exécuter la logique directement dans la console sans avoir à installer et démarrer le service via le gestionnaire de services Windows.
using System.ServiceProcess;
namespace NetworkFileMonitor
{
internal static class Program
{
static void Main()
{
#if DEBUG
var service = new FileMonitorService();
// Utilisation de la réflexion pour appeler OnStart en mode debug si nécessaire,
// ou exposition d'une méthode publique de test.
System.Console.WriteLine("Service en cours d'exécution en mode débogage. Appuyez sur une touche pour arrêter...");
System.Console.ReadKey();
#else
ServiceBase[] ServicesToRun = new ServiceBase[] { new FileMonitorService() };
ServiceBase.Run(ServicesToRun);
#endif
}
}
}