进度条与HttpClient
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进度条与HttpClient相关的知识,希望对你有一定的参考价值。
我有一个文件下载功能:
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();
我怎样才能实现进度条功能?也许到目前为止我可以写出写入的字节数?或者其他的东西?
附:我无法使用DownloadOperation(后台传输),因为来自服务器的数据请求证书 - 并且DownloadOperations中不存在此功能。
最好的方法是使用Windows.Web.Http.HttpClient
而不是System.Net.Http.HttpClient
。第一个支持进步。
但如果由于某种原因你想坚持使用System.Net,你需要实现自己的进步。
删除DataWriter
,删除InMemoryRandomAccessStream
并将HttpCompletionOption.ResponseHeadersRead
添加到GetAsync
调用,以便在收到标题时立即返回,而不是在收到整个响应时返回。即:
// 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();
这是一个自包含的类,它将进行下载,并根据Qazxswpoi上的TheBlueSky和this SO answer上的eriksendc的代码报告进度百分比。
this GitHub comment
用法:
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();
}
From .Net 4.5 onwards: Use 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)
从.Net 4.5开始,您可以使用IProgress<T>
接口处理异步进度报告。您可以使用IProgress<T>
编写一个下载文件的扩展方法,可以这样调用,其中HttpClient
是您的porgress栏或其他UI内容的progress
的实现:
IProgress<float>
履行
此扩展方法的代码如下所示。请注意,此扩展依赖于另一个扩展来处理具有进度报告的异步流复制。
// 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
以上是关于进度条与HttpClient的主要内容,如果未能解决你的问题,请参考以下文章