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