1+ public class Downloader : IDisposable {
2+ private readonly string _downloadUrl ;
3+ private readonly string _destinationFilePath ;
4+
5+ private HttpClient _httpClient = new HttpClient { Timeout = TimeSpan . FromDays ( 1 ) } ;
6+
7+ public delegate void ProgressChangedHandler ( long ? totalFileSize , long totalBytesDownloaded , double ? progressPercentage ) ;
8+
9+ public event ProgressChangedHandler ? ProgressChanged ;
10+
11+ public Downloader ( string downloadUrl , string destinationFilePath ) {
12+ _downloadUrl = downloadUrl ;
13+ _destinationFilePath = destinationFilePath ;
14+ }
15+
16+ public async Task StartDownload ( ) {
17+ //_httpClient = new HttpClient { Timeout = TimeSpan.FromDays(1) };
18+
19+ using ( var response = await _httpClient . GetAsync ( _downloadUrl , HttpCompletionOption . ResponseHeadersRead ) )
20+ await DownloadFileFromHttpResponseMessage ( response ) ;
21+ }
22+
23+ private async Task DownloadFileFromHttpResponseMessage ( HttpResponseMessage response ) {
24+ response . EnsureSuccessStatusCode ( ) ;
25+
26+ var totalBytes = response . Content . Headers . ContentLength ;
27+
28+ using ( var contentStream = await response . Content . ReadAsStreamAsync ( ) )
29+ await ProcessContentStream ( totalBytes , contentStream ) ;
30+ }
31+
32+ private async Task ProcessContentStream ( long ? totalDownloadSize , Stream contentStream ) {
33+ var totalBytesRead = 0L ;
34+ var readCount = 0L ;
35+ var buffer = new byte [ 8192 ] ;
36+ var isMoreToRead = true ;
37+
38+ using ( var fileStream = new FileStream ( _destinationFilePath , FileMode . Create , FileAccess . Write , FileShare . None , 8192 , true ) ) {
39+ do {
40+ var bytesRead = await contentStream . ReadAsync ( buffer , 0 , buffer . Length ) ;
41+ if ( bytesRead == 0 ) {
42+ isMoreToRead = false ;
43+ TriggerProgressChanged ( totalDownloadSize , totalBytesRead ) ;
44+ continue ;
45+ }
46+
47+ await fileStream . WriteAsync ( buffer , 0 , bytesRead ) ;
48+
49+ totalBytesRead += bytesRead ;
50+ readCount += 1 ;
51+
52+ if ( readCount % 100 == 0 )
53+ TriggerProgressChanged ( totalDownloadSize , totalBytesRead ) ;
54+ }
55+ while ( isMoreToRead ) ;
56+ }
57+ }
58+
59+ private void TriggerProgressChanged ( long ? totalDownloadSize , long totalBytesRead ) {
60+ if ( ProgressChanged == null )
61+ return ;
62+
63+ double ? progressPercentage = null ;
64+ if ( totalDownloadSize . HasValue )
65+ progressPercentage = Math . Round ( ( double ) totalBytesRead / totalDownloadSize . Value * 100 , 2 ) ;
66+
67+ ProgressChanged ( totalDownloadSize , totalBytesRead , progressPercentage ) ;
68+ }
69+
70+ public void Dispose ( ) {
71+ _httpClient ? . Dispose ( ) ;
72+ }
73+ }
0 commit comments