diff --git a/src/Downloader.Test/IntegrationTests/DownloadIntegrationTest.cs b/src/Downloader.Test/IntegrationTests/DownloadIntegrationTest.cs index 254ca72..ce635f0 100644 --- a/src/Downloader.Test/IntegrationTests/DownloadIntegrationTest.cs +++ b/src/Downloader.Test/IntegrationTests/DownloadIntegrationTest.cs @@ -1,5 +1,4 @@ using Downloader.DummyHttpServer; -using Downloader.Test.Helper; using Newtonsoft.Json; using System; using System.IO; @@ -9,6 +8,7 @@ using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; +using FileLogger = Downloader.Extensions.Logging.FileLogger; namespace Downloader.Test.IntegrationTests; @@ -805,11 +805,11 @@ public async Task DownloadBigFileOnDisk() { // arrange var totalSize = 1024 * 1024 * 100; // 100MB - //Downloader.AddLogger(new FileLogger($"D:\\TestDownload\\DownloadBigFileOnDisk_{DateTime.Now.ToString("yyyyMMdd.HHmmss")}.log")); Config.ChunkCount = 8; Config.ParallelCount = 8; Config.MaximumBytesPerSecond = 0; URL = DummyFileHelper.GetFileWithNameUrl(Filename, totalSize); + //Downloader.AddLogger(FileLogger.Factory("D:\\TestDownload")); var actualFile = DummyData.GenerateOrderedBytes(totalSize); // act @@ -851,13 +851,13 @@ public async Task DownloadBigFileWithMemoryLimitationOnDisk() { // arrange var totalSize = 1024 * 1024 * 1024; // 1GB - //Downloader.AddLogger(new FileLogger($"D:\\TestDownload\\DownloadBigFileOnDisk_{DateTime.Now.ToString("yyyyMMdd.HHmmss")}.log")); byte fillByte = 123; Config.ChunkCount = 16; Config.ParallelCount = 16; Config.MaximumBytesPerSecond = 0; Config.MaximumMemoryBufferBytes = 1024 * 1024 * 100; // 100MB URL = DummyFileHelper.GetFileWithNameUrl(Filename, totalSize, fillByte); + //Downloader.AddLogger(FileLogger.Factory("D:\\TestDownload")); // act await Downloader.DownloadFileTaskAsync(URL, FilePath); diff --git a/src/Downloader.Test/Helper/FileLogger.cs b/src/Downloader/Extensions/Logging/FileLogger.cs similarity index 81% rename from src/Downloader.Test/Helper/FileLogger.cs rename to src/Downloader/Extensions/Logging/FileLogger.cs index 3382c66..5bf060a 100644 --- a/src/Downloader.Test/Helper/FileLogger.cs +++ b/src/Downloader/Extensions/Logging/FileLogger.cs @@ -1,22 +1,29 @@ -using Downloader.Extensions.Logging; -using System; +using System; using System.Collections.Concurrent; using System.IO; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -namespace Downloader.Test.Helper; +namespace Downloader.Extensions.Logging; -internal class FileLogger : ILogger, IDisposable +public class FileLogger : ILogger, IDisposable { - private volatile bool _disposed = false; - private SemaphoreSlim _semaphore = new SemaphoreSlim(0); + private volatile bool _disposed; + private SemaphoreSlim _semaphore; protected readonly ConcurrentQueue LogQueue; protected string LogPath; protected StreamWriter LogStream; + public static FileLogger Factory(string logPath, [CallerMemberName] string logName = default) + { + var filename = logName + "_" + DateTime.Now.ToString("yyyyMMdd.HHmmss") + ".log"; + return new FileLogger(Path.Combine(logPath, filename)); + } + public FileLogger(string logPath) { + _semaphore = new SemaphoreSlim(0); LogQueue = new ConcurrentQueue(); LogPath = logPath; LogStream = new StreamWriter(FileHelper.CreateFile(logPath)); @@ -105,7 +112,6 @@ private async Task Writer() public void Dispose() { _disposed = true; - LogQueue.Clear(); LogStream?.Dispose(); LogStream = null; } @@ -117,6 +123,6 @@ public async Task FlushAsync() await Task.Delay(100); } - await (LogStream?.FlushAsync() ?? Task.CompletedTask).ConfigureAwait(false); + await (LogStream?.FlushAsync() ?? Task.FromResult(0)).ConfigureAwait(false); } } diff --git a/src/Downloader/Extensions/Logging/ILogger.cs b/src/Downloader/Extensions/Logging/ILogger.cs index d969b55..3350d67 100644 --- a/src/Downloader/Extensions/Logging/ILogger.cs +++ b/src/Downloader/Extensions/Logging/ILogger.cs @@ -3,7 +3,7 @@ namespace Downloader.Extensions.Logging; -public interface ILogger +public interface ILogger { void Debug(string message); void Info(string message); diff --git a/src/Downloader/IDownloadService.cs b/src/Downloader/IDownloadService.cs index a495bbb..d080b77 100644 --- a/src/Downloader/IDownloadService.cs +++ b/src/Downloader/IDownloadService.cs @@ -4,165 +4,171 @@ using System.Threading; using System.Threading.Tasks; -namespace Downloader +namespace Downloader; + +public interface IDownloadService { - public interface IDownloadService - { - /// - /// Gets a value indicating whether the download operation is currently in progress. - /// - bool IsBusy { get; } - - /// - /// Gets a value indicating whether the download operation has been cancelled. - /// - bool IsCancelled { get; } - - /// - /// Gets the DownloadPackage object that contains information about the file to download. - /// - DownloadPackage Package { get; } - - /// - /// Gets the current status of the download operation as a DownloadStatus enum value. - /// - DownloadStatus Status { get; } - - /// - /// Event that is raised when the download operation is completed. - /// The event handler is passed an AsyncCompletedEventArgs object that contains - /// information about the completion status of the operation. - /// - event EventHandler DownloadFileCompleted; - - /// - /// Event that is raised periodically during the download operation to report the progress of the download. - /// The event handler is passed a DownloadProgressChangedEventArgs object that contains - /// information about the progress of the download, such as the number of bytes downloaded and the total file size. - /// - event EventHandler DownloadProgressChanged; - - /// - /// Event that is raised periodically during the download operation to report the progress of a single chunk download. - /// The event handler is passed a DownloadProgressChangedEventArgs object that contains - /// information about the progress of the chunk download, such as the number of bytes downloaded and the total chunk size. - /// - event EventHandler ChunkDownloadProgressChanged; - - /// - /// Event that is raised when the download operation starts. - /// The event handler is passed a DownloadStartedEventArgs object that contains - /// information about the download operation, such as the download URL and the local file path. - /// - event EventHandler DownloadStarted; - - /// - /// Asynchronously resume downloads a file and returns a Stream object that contains the downloaded file data. - /// - /// The DownloadPackage object that contains information about the file to download. - /// A cancellation token that can be used to cancel the download operation. - /// A Task object that represents the asynchronous download operation. - Task DownloadFileTaskAsync(DownloadPackage package, CancellationToken cancellationToken = default); - - /// - /// Asynchronously resume downloads a file from the specified URL and returns a Stream object that contains the downloaded file data. - /// - /// The DownloadPackage object that contains information about the file to download. - /// The download URL of the file to download. - /// A cancellation token that can be used to cancel the download operation. - /// A Task object that represents the asynchronous download operation. - Task DownloadFileTaskAsync(DownloadPackage package, string address, CancellationToken cancellationToken = default); - - /// - /// Asynchronously resume downloads a file from the specified URL and returns a Stream object that contains the downloaded file data. - /// - /// The DownloadPackage object that contains information about the file to download. - /// The download URLs of a file to download as parallel with mirror links. - /// A cancellation token that can be used to cancel the download operation. - /// A Task object that represents the asynchronous download operation. - Task DownloadFileTaskAsync(DownloadPackage package, string[] urls, CancellationToken cancellationToken = default); - - /// - /// Asynchronously downloads a file from the specified URL and returns a Stream object that contains the downloaded file data. - /// - /// The download URL of the file to download. - /// A cancellation token that can be used to cancel the download operation. - /// A Task object that represents the asynchronous download operation. - Task DownloadFileTaskAsync(string address, CancellationToken cancellationToken = default); - - /// - /// Asynchronously downloads a file from the specified URL and returns a Stream object that contains the downloaded file data. - /// - /// The download URLs of a file to download as parallel with mirror links. - /// A cancellation token that can be used to cancel the download operation. - /// A Task object that represents the asynchronous download operation. - Task DownloadFileTaskAsync(string[] urls, CancellationToken cancellationToken = default); - - /// - /// Asynchronously downloads a file from the specified URL and saves it to the specified file name. - /// - /// The download URL of the file to download. - /// The local file name to save the downloaded file as. - /// A cancellation token that can be used to cancel the download operation. - /// A Task object that represents the asynchronous download operation. - Task DownloadFileTaskAsync(string address, string fileName, CancellationToken cancellationToken = default); - - /// - /// Asynchronously downloads a file from the specified URL and saves it to the specified file name. - /// - /// The download URLs of a file to download as parallel with mirror links. - /// The local file name to save the downloaded file as. - /// A cancellation token that can be used to cancel the download operation. - /// A Task object that represents the asynchronous download operation. - Task DownloadFileTaskAsync(string[] urls, string fileName, CancellationToken cancellationToken = default); - - /// - /// Asynchronously downloads a file from the specified URL and saves it to the specified directory. - /// - /// The download URL of the file to download. - /// The local directory to save the downloaded file in. - /// A cancellation token that can be used to cancel the download operation. - /// A Task object that represents the asynchronous download operation. - Task DownloadFileTaskAsync(string address, DirectoryInfo folder, CancellationToken cancellationToken = default); - - /// - /// Asynchronously downloads a file from the specified URL and saves it to the specified directory. - /// - /// The download URLs of a file to download as parallel with mirror links. - /// The local directory to save the downloaded file in. - /// A cancellation token that can be used to cancel the download operation. - /// A Task object that represents the asynchronous download operation. - Task DownloadFileTaskAsync(string[] urls, DirectoryInfo folder, CancellationToken cancellationToken = default); - - /// - /// Cancels the current download operation asynchronously. - /// - void CancelAsync(); - - /// - /// Cancels the current download operation asynchronously and returns a Task object that represents the cancellation operation. - /// - /// A Task object that represents the asynchronous cancellation operation. - Task CancelTaskAsync(); - - /// - /// Pauses the current download operation. In this way, you can resume the download very quickly. - /// - /// - /// Note: Please use the cancel method instead of this method - /// if you want to stop and dispose of the download and save the download package until you can resume it again. - /// - void Pause(); - - /// - /// Resumes a paused download operation. - /// - void Resume(); - - /// - /// Clears any data related to the current download operation. - /// - /// A Task object that represents the asynchronous clearing operation. - Task Clear(); - } + /// + /// Gets a value indicating whether the download operation is currently in progress. + /// + bool IsBusy { get; } + + /// + /// Gets a value indicating whether the download operation has been cancelled. + /// + bool IsCancelled { get; } + + /// + /// Gets the DownloadPackage object that contains information about the file to download. + /// + DownloadPackage Package { get; } + + /// + /// Gets the current status of the download operation as a DownloadStatus enum value. + /// + DownloadStatus Status { get; } + + /// + /// Event that is raised when the download operation is completed. + /// The event handler is passed an AsyncCompletedEventArgs object that contains + /// information about the completion status of the operation. + /// + event EventHandler DownloadFileCompleted; + + /// + /// Event that is raised periodically during the download operation to report the progress of the download. + /// The event handler is passed a DownloadProgressChangedEventArgs object that contains + /// information about the progress of the download, such as the number of bytes downloaded and the total file size. + /// + event EventHandler DownloadProgressChanged; + + /// + /// Event that is raised periodically during the download operation to report the progress of a single chunk download. + /// The event handler is passed a DownloadProgressChangedEventArgs object that contains + /// information about the progress of the chunk download, such as the number of bytes downloaded and the total chunk size. + /// + event EventHandler ChunkDownloadProgressChanged; + + /// + /// Event that is raised when the download operation starts. + /// The event handler is passed a DownloadStartedEventArgs object that contains + /// information about the download operation, such as the download URL and the local file path. + /// + event EventHandler DownloadStarted; + + /// + /// Asynchronously resume downloads a file and returns a Stream object that contains the downloaded file data. + /// + /// The DownloadPackage object that contains information about the file to download. + /// A cancellation token that can be used to cancel the download operation. + /// A Task object that represents the asynchronous download operation. + Task DownloadFileTaskAsync(DownloadPackage package, CancellationToken cancellationToken = default); + + /// + /// Asynchronously resume downloads a file from the specified URL and returns a Stream object that contains the downloaded file data. + /// + /// The DownloadPackage object that contains information about the file to download. + /// The download URL of the file to download. + /// A cancellation token that can be used to cancel the download operation. + /// A Task object that represents the asynchronous download operation. + Task DownloadFileTaskAsync(DownloadPackage package, string address, CancellationToken cancellationToken = default); + + /// + /// Asynchronously resume downloads a file from the specified URL and returns a Stream object that contains the downloaded file data. + /// + /// The DownloadPackage object that contains information about the file to download. + /// The download URLs of a file to download as parallel with mirror links. + /// A cancellation token that can be used to cancel the download operation. + /// A Task object that represents the asynchronous download operation. + Task DownloadFileTaskAsync(DownloadPackage package, string[] urls, CancellationToken cancellationToken = default); + + /// + /// Asynchronously downloads a file from the specified URL and returns a Stream object that contains the downloaded file data. + /// + /// The download URL of the file to download. + /// A cancellation token that can be used to cancel the download operation. + /// A Task object that represents the asynchronous download operation. + Task DownloadFileTaskAsync(string address, CancellationToken cancellationToken = default); + + /// + /// Asynchronously downloads a file from the specified URL and returns a Stream object that contains the downloaded file data. + /// + /// The download URLs of a file to download as parallel with mirror links. + /// A cancellation token that can be used to cancel the download operation. + /// A Task object that represents the asynchronous download operation. + Task DownloadFileTaskAsync(string[] urls, CancellationToken cancellationToken = default); + + /// + /// Asynchronously downloads a file from the specified URL and saves it to the specified file name. + /// + /// The download URL of the file to download. + /// The local file name to save the downloaded file as. + /// A cancellation token that can be used to cancel the download operation. + /// A Task object that represents the asynchronous download operation. + Task DownloadFileTaskAsync(string address, string fileName, CancellationToken cancellationToken = default); + + /// + /// Asynchronously downloads a file from the specified URL and saves it to the specified file name. + /// + /// The download URLs of a file to download as parallel with mirror links. + /// The local file name to save the downloaded file as. + /// A cancellation token that can be used to cancel the download operation. + /// A Task object that represents the asynchronous download operation. + Task DownloadFileTaskAsync(string[] urls, string fileName, CancellationToken cancellationToken = default); + + /// + /// Asynchronously downloads a file from the specified URL and saves it to the specified directory. + /// + /// The download URL of the file to download. + /// The local directory to save the downloaded file in. + /// A cancellation token that can be used to cancel the download operation. + /// A Task object that represents the asynchronous download operation. + Task DownloadFileTaskAsync(string address, DirectoryInfo folder, CancellationToken cancellationToken = default); + + /// + /// Asynchronously downloads a file from the specified URL and saves it to the specified directory. + /// + /// The download URLs of a file to download as parallel with mirror links. + /// The local directory to save the downloaded file in. + /// A cancellation token that can be used to cancel the download operation. + /// A Task object that represents the asynchronous download operation. + Task DownloadFileTaskAsync(string[] urls, DirectoryInfo folder, CancellationToken cancellationToken = default); + + /// + /// Cancels the current download operation asynchronously. + /// + void CancelAsync(); + + /// + /// Cancels the current download operation asynchronously and returns a Task object that represents the cancellation operation. + /// + /// A Task object that represents the asynchronous cancellation operation. + Task CancelTaskAsync(); + + /// + /// Pauses the current download operation. In this way, you can resume the download very quickly. + /// + /// + /// Note: Please use the cancel method instead of this method + /// if you want to stop and dispose of the download and save the download package until you can resume it again. + /// + void Pause(); + + /// + /// Resumes a paused download operation. + /// + void Resume(); + + /// + /// Clears any data related to the current download operation. + /// + /// A Task object that represents the asynchronous clearing operation. + Task Clear(); + + /// + /// Add logger class to log the Downloader events + /// + /// + + void AddLogger(Extensions.Logging.ILogger logger); } \ No newline at end of file diff --git a/src/Samples/Downloader.Sample/DownloadItem.cs b/src/Samples/Downloader.Sample/DownloadItem.cs index 6fee277..4ece55f 100644 --- a/src/Samples/Downloader.Sample/DownloadItem.cs +++ b/src/Samples/Downloader.Sample/DownloadItem.cs @@ -1,8 +1,12 @@ -namespace Downloader.Sample; +using System.IO; + +namespace Downloader.Sample; public class DownloadItem { - public string FolderPath { get; set; } + public string _folderPath; + + public string FolderPath { get => _folderPath ?? Path.GetDirectoryName(FileName); set => _folderPath = value; } public string FileName { get; set; } public string Url { get; set; } } \ No newline at end of file diff --git a/src/Samples/Downloader.Sample/Program.cs b/src/Samples/Downloader.Sample/Program.cs index 13b6a3a..c3c720a 100644 --- a/src/Samples/Downloader.Sample/Program.cs +++ b/src/Samples/Downloader.Sample/Program.cs @@ -8,6 +8,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using FileLogger = Downloader.Extensions.Logging.FileLogger; namespace Downloader.Sample; @@ -143,13 +144,14 @@ private static async Task DownloadFile(DownloadItem downloadIt { CurrentDownloadConfiguration = GetDownloadConfiguration(); CurrentDownloadService = CreateDownloadService(CurrentDownloadConfiguration); - if (string.IsNullOrWhiteSpace(downloadItem.FileName)) { + CurrentDownloadService.AddLogger(FileLogger.Factory(downloadItem.FolderPath)); await CurrentDownloadService.DownloadFileTaskAsync(downloadItem.Url, new DirectoryInfo(downloadItem.FolderPath)).ConfigureAwait(false); } else { + CurrentDownloadService.AddLogger(FileLogger.Factory(downloadItem.FolderPath, Path.GetFileName(downloadItem.FileName))); await CurrentDownloadService.DownloadFileTaskAsync(downloadItem.Url, downloadItem.FileName).ConfigureAwait(false); }