/// <summary> /// Cleans files that failed to be deleted during previous delete or clean operations. If another thread/process is currently running a clean /// operation on the folder assigned to this image host, an <see cref="IOException"/> is thrown. /// </summary> /// <param name="cancellationToken">An optional cancellation token to cancel the operation.</param> /// <exception cref="InvalidOperationException"> /// This image host does not support cleanup. /// </exception> /// <exception cref="IOException"> /// Another thread/process is already running a cleaning operation on the base directory. /// </exception> public void Clean(CancellationToken cancellationToken = default) { if (CleanupDirectory == null) { throw new InvalidOperationException(CleanupNotSupportedMessage); } var cleanLockFile = CleanupDirectory.CombineFile(".lock", PathOptions.None); FileStream cleanLock; try { cleanLock = cleanLockFile.OpenStream(FileMode.OpenOrCreate, options: FileOptions.DeleteOnClose); } catch (IOException ex) { throw new IOException($"Could not obtain a lock on '{cleanLockFile.PathDisplay}'. Another thread or process may be cleaning right now.", ex); } using (cleanLock) { foreach (var cleanupRecord in CleanupDirectory.GetChildFiles("*.delete")) { if (cancellationToken.IsCancellationRequested) { throw new OperationCanceledException(cancellationToken); } var imageId = Guid.Parse(cleanupRecord.NameWithoutExtension); try { DeleteWithAggregateThrow(imageId); } catch (AggregateException ex) { using var fs = cleanupRecord.OpenStream(FileMode.Truncate); using var sw = new StreamWriter(fs); WriteCleanupRecord(sw, "IMAGE FILE CLEANUP FAILED", ex); continue; } try { cleanupRecord.Delete(); } catch (FileNotFoundException) { } catch (Exception ex) { try { const string header = "CLEANUP RECORD REMOVAL FAILED"; using var fs = cleanupRecord.OpenStream(FileMode.Truncate); using var sw = new StreamWriter(fs); WriteCleanupRecord(sw, header, ex); } catch (FileNotFoundException) { } } } } }
/// <summary> /// Gets a value indicating whether there are files that need to be cleaned up. This method is thread-safe. /// </summary> public bool NeedsCleaning() { return(CleanupDirectory != null? CleanupDirectory.GetChildFiles("*.delete").Any() : throw new InvalidOperationException("Cleanup is not supported.")); }