π Fast, cross-platform, and reliable multipart downloader in .Net
π
Downloader is a modern, fluent, asynchronous, and portable library for .NET, built with testability in mind. It supports multipart downloads with real-time asynchronous progress events. The library is compatible with projects targeting .NET Standard 2.1
, .NET 8
, and later versions.
Downloader works on Windows, Linux, and macOS.
Note: Support for older versions of .NET was removed in Downloader
v3.2.0
. From this version onwards, only.Net 8.0
and higher versions are supported.
If you need compatibility with older .NET versions (e.g.,.NET Framework 4.6.1
), use Downloaderv3.1.*
.
For a complete example, see the Downloader.Sample project in this repository.
- Simple interface for download requests.
- Asynchronous, non-blocking file downloads.
- Supports all file types (e.g., images, videos, PDFs, APKs).
- Cross-platform support for files of any size.
- Real-time progress updates for each download chunk.
- Downloads files in multiple parts (parallel download).
- Resilient to client-side and server-side errors.
- Configurable
ChunkCount
to control download segmentation. - Supports both in-memory and on-disk multipart downloads.
- Parallel saving of chunks directly into the final file (no temporary files).
- Pre-allocates file size before download begins.
- Ability to resume downloads with a saved package object.
- Provides real-time speed and progress data.
- Asynchronous pause and resume functionality.
- Download files with dynamic speed limits.
- Supports downloading to memory streams (without saving to disk).
- Supports large file downloads and live-streaming (e.g., music playback during download).
- Download a specific byte range from a large file.
- Lightweight, fast codebase with no external dependencies.
- Manage RAM usage during downloads.
Installation via NuGet
PM> Install-Package Downloader
dotnet add package Downloader
var downloadOpt = new DownloadConfiguration()
{
ChunkCount = 8, // Number of file parts, default is 1
ParallelDownload = true // Download parts in parallel (default is false)
};
Note: Only include the options you need in your application.
var downloadOpt = new DownloadConfiguration()
{
// usually, hosts support max to 8000 bytes, default value is 8000
BufferBlockSize = 10240,
// file parts to download, the default value is 1
ChunkCount = 8,
// download speed limited to 2MB/s, default values is zero or unlimited
MaximumBytesPerSecond = 1024*1024*2,
// the maximum number of times to fail
MaxTryAgainOnFailure = 5,
// release memory buffer after each 50 MB
MaximumMemoryBufferBytes = 1024 * 1024 * 50,
// download parts of the file as parallel or not. The default value is false
ParallelDownload = true,
// number of parallel downloads. The default value is the same as the chunk count
ParallelCount = 4,
// timeout (millisecond) per stream block reader, default values is 1000
Timeout = 1000,
// set true if you want to download just a specific range of bytes of a large file
RangeDownload = false,
// floor offset of download range of a large file
RangeLow = 0,
// ceiling offset of download range of a large file
RangeHigh = 0,
// clear package chunks data when download completed with failure, default value is false
ClearPackageOnCompletionWithFailure = true,
// minimum size of chunking to download a file in multiple parts, the default value is 512
MinimumSizeOfChunking = 1024,
// Before starting the download, reserve the storage space of the file as file size, the default value is false
ReserveStorageSpaceBeforeStartingDownload = true,
// Get on demand downloaded data with ReceivedBytes on downloadProgressChanged event
EnableLiveStreaming = false,
// config and customize request headers
RequestConfiguration =
{
Accept = "*/*",
CookieContainer = cookies,
Headers = ["Accept-Encoding: gzip, deflate, br"], // { your custom headers }
KeepAlive = true, // default value is false
ProtocolVersion = HttpVersion.Version11, // default value is HTTP 1.1
// your custom user agent or your_app_name/app_version.
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
Proxy = new WebProxy() {
Address = new Uri("http://YourProxyServer/proxy.pac"),
UseDefaultCredentials = false,
Credentials = System.Net.CredentialCache.DefaultNetworkCredentials,
BypassProxyOnLocal = true
},
Authorization = new AuthenticationHeaderValue("Bearer", "token");
}
};
var downloader = new DownloadService(downloadOpt);
// Provide `FileName` and `TotalBytesToReceive` at the start of each download
downloader.DownloadStarted += OnDownloadStarted;
// Provide any information about chunker downloads,
// like progress percentage per chunk, speed,
// total received bytes and received bytes array to live streaming.
downloader.ChunkDownloadProgressChanged += OnChunkDownloadProgressChanged;
// Provide any information about download progress,
// like progress percentage of sum of chunks, total speed,
// average speed, total received bytes and received bytes array
// to live streaming.
downloader.DownloadProgressChanged += OnDownloadProgressChanged;
// Download completed event that can include errors or
// canceled or download completed successfully.
downloader.DownloadFileCompleted += OnDownloadFileCompleted;
string file = @"Your_Path\fileName.zip";
string url = @"https://file-examples.com/fileName.zip";
await downloader.DownloadFileTaskAsync(url, file);
DirectoryInfo path = new DirectoryInfo("Your_Path");
string url = @"https://file-examples.com/fileName.zip";
// download into "Your_Path\fileName.zip"
await downloader.DownloadFileTaskAsync(url, path);
// After download completion, it gets a MemoryStream
Stream destinationStream = await downloader.DownloadFileTaskAsync(url);
When you want to resume a download quickly after pausing a few seconds. You can call the Pause
function of the downloader service. This way, streams stay alive and are only suspended by a locker to be released and resumed whenever you want.
// Pause the download
DownloadService.Pause();
// Resume the download
DownloadService.Resume();
The DownloadService
class has a property called Package
that stores each step of the download. To stop the download you must call the CancelAsync
method. Now, if you want to continue again, you must call the same DownloadFileTaskAsync
function with the Package
parameter to resume your download. For example:
// At first, keep and store the Package file to resume
// your download from the last download position:
DownloadPackage pack = downloader.Package;
Stop or cancel download:
// This function breaks your stream and cancels progress.
downloader.CancelAsync();
Resuming download after cancellation:
await downloader.DownloadFileTaskAsync(pack);
So that you can even save your large downloads with a very small amount in the Package and after restarting the program, restore it and start continuing your download. The packages are your snapshot of the download instance. Only the downloaded file addresses will be included in the package, and you can resume it whenever you want. For more detail see StopResumeDownloadTest method
Note: Sometimes a server does not support downloading in a specific range. That time, we can't resume downloads after canceling. So, the downloader starts from the beginning.
For easy and fluent use of the downloader, you can use the DownloadBuilder
class. Consider the following examples:
Simple usage:
await DownloadBuilder.New()
.WithUrl(@"https://host.com/test-file.zip")
.WithDirectory(@"C:\temp")
.Build()
.StartAsync();
Complex usage:
IDownload download = DownloadBuilder.New()
.WithUrl(@"https://host.com/test-file.zip")
.WithDirectory(@"C:\temp")
.WithFileName("test-file.zip")
.WithConfiguration(new DownloadConfiguration())
.Build();
download.DownloadProgressChanged += DownloadProgressChanged;
download.DownloadFileCompleted += DownloadFileCompleted;
download.DownloadStarted += DownloadStarted;
download.ChunkDownloadProgressChanged += ChunkDownloadProgressChanged;
await download.StartAsync();
download.Stop(); // cancel current download
Resume the existing download package:
await DownloadBuilder.Build(package).StartAsync();
Resume the existing download package with a new configuration:
await DownloadBuilder.Build(package, config).StartAsync();
var download = DownloadBuilder.New()
.Build()
.WithUrl(url)
.WithFileLocation(path);
await download.StartAsync();
download.Pause(); // pause current download quickly
download.Resume(); // continue current download quickly
< 8000 div class="markdown-heading" dir="auto">