/// <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) { } } } } }
private CleanupDirectory ReadCleanupDirectory(XElement item) { CleanupDirectory result = new CleanupDirectory { LocalDirectory = XElementExtender.ReadPath(item) }; OutputConsole.PrintVerbose(result, 5); return(result); }
/// <summary> /// Initializes a new instance of the <see cref="ImageHost"/> class. /// </summary> /// <param name="baseDirectory">The base directory path where images are stored. Multiple image host instances (either in the same process or in /// different processes) can safely share the same directory but it should not be used by anything other than image hosts.</param> /// <param name="options">Options for configuring the image host.</param> public ImageHost(IAbsoluteDirectoryPath baseDirectory, ImageHostOptions options) { if (_jpegEncoder == null) { throw new InvalidOperationException("JPEG encoder was not found."); } BaseDirectory = baseDirectory; DeleteFailureMode = options.DeleteFailureMode; BaseDirectory.Create(); if (options.DeleteFailureMode == DeleteFailureMode.WriteCleanupRecord) { CleanupDirectory = baseDirectory.CombineDirectory(".cleanup", PathOptions.None); CleanupDirectory.Create(); } }
/// <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.")); }