본문 바로가기
C#

[C#] HttpClient Download With Progress

by Jcoder 2022. 6. 29.
public class HttpClientDownloadWithProgress : IDisposable
{
    private string _downloadUrl;
    private string _destinationFilePath;
    private bool disposedValue;
    private readonly HttpClient _httpClient = new HttpClient { Timeout = TimeSpan.FromDays(1) };

    public string DownloadUrl { get => _downloadUrl; set => _downloadUrl = value; }
    public string DestinationFilePath { get => _destinationFilePath; set => _destinationFilePath = value; }

    public delegate void ProgressChangedHandler(long? totalFileSize, long totalBytesDownloaded, double? progressPercentage);

    public event ProgressChangedHandler ProgressChanged;

	public HttpClientDownloadWithProgress()
    {
        
    }
    
    public HttpClientDownloadWithProgress(string downloadUrl, string destinationFilePath)
    {
        DownloadUrl = downloadUrl;
        DestinationFilePath = destinationFilePath;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: 관리형 상태(관리형 개체)를 삭제합니다.
                _httpClient?.Dispose();
            }

            // TODO: 비관리형 리소스(비관리형 개체)를 해제하고 종료자를 재정의합니다.
            // TODO: 큰 필드를 null로 설정합니다.
            disposedValue = true;
        }
    }

    // // TODO: 비관리형 리소스를 해제하는 코드가 'Dispose(bool disposing)'에 포함된 경우에만 종료자를 재정의합니다.
    // ~HttpClientDownloadWithProgress()
    // {
    //     // 이 코드를 변경하지 마세요. 'Dispose(bool disposing)' 메서드에 정리 코드를 입력합니다.
    //     Dispose(disposing: false);
    // }

    public void Dispose()
    {
        // 이 코드를 변경하지 마세요. 'Dispose(bool disposing)' 메서드에 정리 코드를 입력합니다.
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }

    public async Task StartDownload()
    {
        if (string.IsNullOrEmpty(_downloadUrl) || string.IsNullOrEmpty(_destinationFilePath))
            throw new ArgumentNullException($"{nameof(DownloadUrl)} 또는 {nameof(DestinationFilePath)}", $"값이 비어있습니다.");

        using (var response = await _httpClient.GetAsync(DownloadUrl, HttpCompletionOption.ResponseHeadersRead))
            await DownloadFileFromHttpResponseMessage(response);
    }

    private async Task DownloadFileFromHttpResponseMessage(HttpResponseMessage response)
    {
        response.EnsureSuccessStatusCode();

        var totalBytes = response.Content.Headers.ContentLength;

        using (var contentStream = await response.Content.ReadAsStreamAsync())
            await ProcessContentStream(totalBytes, contentStream);
    }

    private async Task ProcessContentStream(long? totalDownloadSize, Stream contentStream)
    {
        var totalBytesRead = 0L;
        var readCount = 0L;
        var buffer = new byte[8192];
        var isMoreToRead = true;

        using (var fileStream = new FileStream(DestinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
        {
            do
            {
                var bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);
                if (bytesRead == 0)
                {
                    isMoreToRead = false;
                    TriggerProgressChanged(totalDownloadSize, totalBytesRead);
                    continue;
                }

                await fileStream.WriteAsync(buffer, 0, bytesRead);

                totalBytesRead += bytesRead;
                readCount += 1;

                if (readCount % 100 == 0)
                    TriggerProgressChanged(totalDownloadSize, totalBytesRead);
            }
            while (isMoreToRead);
        }
    }

    private void TriggerProgressChanged(long? totalDownloadSize, long totalBytesRead)
    {
        if (ProgressChanged == null)
            return;

        double? progressPercentage = null;
        if (totalDownloadSize.HasValue)
            progressPercentage = Math.Round((double)totalBytesRead / totalDownloadSize.Value * 100, 2);

        ProgressChanged(totalDownloadSize, totalBytesRead, progressPercentage);
    }
}

 

참고 : https://github.com/dotnet/runtime/issues/16681#issuecomment-195980023

 

Progress while downloading a large file with HttpClient · Issue #16681 · dotnet/runtime

Hi All, Googling around I found that Windows.Web.Http.HttpClient provides a way to get progress of a download. Is there an way to get some kind of progress information when downloading a large file...

github.com

참고 : c# - Progress bar with HttpClient - Stack Overflow

 

Progress bar with HttpClient

i have a file downloader function: HttpClientHandler aHandler = new HttpClientHandler(); aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic; HttpClient a...

stackoverflow.com