进度条与HttpClient

 rockminer 发布于 2023-02-09 14:05

我有一个文件下载功能:

        HttpClientHandler aHandler = new HttpClientHandler();
        aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
        HttpClient aClient = new HttpClient(aHandler);
        aClient.DefaultRequestHeaders.ExpectContinue = false;
        HttpResponseMessage response = await aClient.GetAsync(url);
        InMemoryRandomAccessStream randomAccessStream = new InMemoryRandomAccessStream();

        // To save downloaded image to local storage
        var imageFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(
        filename, CreationCollisionOption.ReplaceExisting);
        var fs = await imageFile.OpenAsync(FileAccessMode.ReadWrite);
        DataWriter writer = new DataWriter(fs.GetOutputStreamAt(0));

        writer.WriteBytes(await response.Content.ReadAsByteArrayAsync());

        await writer.StoreAsync();
        //current.image.SetSource(randomAccessStream);
        writer.DetachStream();
        await fs.FlushAsync();

我怎样才能实现进度条功能?也许到目前为止我可以写出写入的字节数?或者其他的东西?

PS我无法使用DownloadOperation(后台传输),因为来自服务器的数据请求证书 - 并且DownloadOperations中不存在此功能.

3 个回答
  • 最好的方法是使用Windows.Web.Http.HttpClient而不是System.Net.Http.HttpClient.第一个支持进步.

    但如果由于某种原因你想坚持使用System.Net,你需要实现自己的进步.

    删除DataWriter,删除InMemoryRandomAccessStream添加HttpCompletionOption.ResponseHeadersReadGetAsync调用,以便在收到标题时立即返回,而不是在收到整个响应时返回.即:

    // Your original code.
    HttpClientHandler aHandler = new HttpClientHandler();
    aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
    HttpClient aClient = new HttpClient(aHandler);
    aClient.DefaultRequestHeaders.ExpectContinue = false;
    HttpResponseMessage response = await aClient.GetAsync(
        url,
        HttpCompletionOption.ResponseHeadersRead); // Important! ResponseHeadersRead.
    
    // To save downloaded image to local storage
    var imageFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(
        filename,
        CreationCollisionOption.ReplaceExisting);
    var fs = await imageFile.OpenAsync(FileAccessMode.ReadWrite);
    
    // New code.
    Stream stream = await response.Content.ReadAsStreamAsync();
    IInputStream inputStream = stream.AsInputStream();
    ulong totalBytesRead = 0;
    while (true)
    {
        // Read from the web.
        IBuffer buffer = new Windows.Storage.Streams.Buffer(1024);
        buffer = await inputStream.ReadAsync(
            buffer,
            buffer.Capacity,
            InputStreamOptions.None);
    
        if (buffer.Length == 0)
        {
            // There is nothing else to read.
            break;
        }
    
        // Report progress.
        totalBytesRead += buffer.Length;
        System.Diagnostics.Debug.WriteLine("Bytes read: {0}", totalBytesRead);
    
        // Write to file.
        await fs.WriteAsync(buffer);
    }
    inputStream.Dispose();
    fs.Dispose();
    

    2023-02-09 14:07 回答
  • 从.Net 4.5开始:使用 IProgress<T>

    从.Net 4.5开始,您可以使用该IProgress<T>界面处理异步进度报告.你可以写在下载使用这些文件的扩展方法HttpClient,可以这样调用这里progress是执行IProgress<float>您porgress酒吧或其他UI的东西:

    // Seting up the http client used to download the data
    using (var client = new HttpClient()) {
        client.Timeout = TimeSpan.FromMinutes(5);
    
        // Create a file stream to store the downloaded data.
        // This really can be any type of writeable stream.
        using (var file = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) {
    
            // Use the custom extension method below to download the data.
            // The passed progress-instance will receive the download status updates.
            await client.DownloadAsync(DownloadUrl, file, progress, cancellationToken);
        }
    }
    

    履行

    此扩展方法的代码如下所示.请注意,此扩展依赖于另一个扩展来处理具有进度报告的异步流复制.

    public static class HttpClientExtensions
    {
        public static async Task DownloadAsync(this HttpClient client, string requestUri, Stream destination, IProgress<float> progress = null, CancellationToken cancellationToken = default) {
            // Get the http headers first to examine the content length
            using (var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead)) {
                var contentLength = response.Content.Headers.ContentLength;
    
                using (var download = await response.Content.ReadAsStreamAsync()) {
    
                    // Ignore progress reporting when no progress reporter was 
                    // passed or when the content length is unknown
                    if (progress == null || !contentLength.HasValue) {
                        await download.CopyToAsync(destination);
                        return;
                    }
    
                    // Convert absolute progress (bytes downloaded) into relative progress (0% - 100%)
                    var relativeProgress = new Progress<long>(totalBytes => progress.Report((float)totalBytes / contentLength.Value));
                    // Use extension method to report progress while downloading
                    await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken);
                    progress.Report(1);
                }
            }
        }
    }
    

    通过流扩展实现真正的进度报告:

    public static class StreamExtensions
    {
        public static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long> progress = null, CancellationToken cancellationToken = default) {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (!source.CanRead)
                throw new ArgumentException("Has to be readable", nameof(source));
            if (destination == null)
                throw new ArgumentNullException(nameof(destination));
            if (!destination.CanWrite)
                throw new ArgumentException("Has to be writable", nameof(destination));
            if (bufferSize < 0)
                throw new ArgumentOutOfRangeException(nameof(bufferSize));
    
            var buffer = new byte[bufferSize];
            long totalBytesRead = 0;
            int bytesRead;
            while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) {
                await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
                totalBytesRead += bytesRead;
                progress?.Report(totalBytesRead);
            }
        }
    }
    

    2023-02-09 14:07 回答
  • 这是一个自包含的类,它将进行下载,并根据TheSlueSky在此SO答案中的代码报告进度百分比,并在此GitHub评论中报告eriksendc.

    public class HttpClientDownloadWithProgress : IDisposable
    {
        private readonly string _downloadUrl;
        private readonly string _destinationFilePath;
    
        private HttpClient _httpClient;
    
        public delegate void ProgressChangedHandler(long? totalFileSize, long totalBytesDownloaded, double? progressPercentage);
    
        public event ProgressChangedHandler ProgressChanged;
    
        public HttpClientDownloadWithProgress(string downloadUrl, string destinationFilePath)
        {
            _downloadUrl = downloadUrl;
            _destinationFilePath = destinationFilePath;
        }
    
        public async Task StartDownload()
        {
            _httpClient = new HttpClient { Timeout = TimeSpan.FromDays(1) };
    
            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);
        }
    
        public void Dispose()
        {
            _httpClient?.Dispose();
        }
    }
    

    用法:

    var downloadFileUrl = "http://example.com/file.zip";
    var destinationFilePath = Path.GetFullPath("file.zip");
    
    using (var client = new HttpClientDownloadWithProgress(downloadFileUrl, destinationFilePath))
    {
        client.ProgressChanged += (totalFileSize, totalBytesDownloaded, progressPercentage) => {
            Console.WriteLine($"{progressPercentage}% ({totalBytesDownloaded}/{totalFileSize})");
        };
    
        await client.StartDownload();
    }
    

    结果:

    7.81% (26722304/342028776)
    8.05% (27535016/342028776)
    8.28% (28307984/342028776)
    8.5% (29086548/342028776)
    8.74% (29898692/342028776)
    8.98% (30704184/342028776)
    9.22% (31522816/342028776)
    

    2023-02-09 14:07 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有