Che*_*ese 24 c# download windows-8 windows-runtime windows-8.1
我有一个文件下载功能:
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();
Run Code Online (Sandbox Code Playgroud)
我怎样才能实现进度条功能?也许到目前为止我可以写出写入的字节数?或者其他的东西?
PS我无法使用DownloadOperation(后台传输),因为来自服务器的数据请求证书 - 并且DownloadOperations中不存在此功能.
kie*_*wic 24
最好的方法是使用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();
Run Code Online (Sandbox Code Playgroud)
Ren*_*ers 23
这是一个自包含的类,它将进行下载,并根据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();
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
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();
}
Run Code Online (Sandbox Code Playgroud)
结果:
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)
Run Code Online (Sandbox Code Playgroud)
Bru*_*ell 19
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);
}
}
Run Code Online (Sandbox Code Playgroud)
此扩展方法的代码如下所示.请注意,此扩展依赖于另一个扩展来处理具有进度报告的异步流复制.
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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
通过流扩展实现真正的进度报告:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
cae*_*say 10
实现上传和下载进度跟踪的最简单方法是使用Microsoft.AspNet.WebApi.Client nuget 包中的ProgressMessageHandler。
注:该库原名为System.Net.Http.Formatting
,后更名为Microsoft.AspNet.WebApi.Client
. 但是,该库与 ASP.Net 无关,任何寻求 Microsoft 官方扩展的项目都可以使用HttpClient
. 源代码可以在这里找到。
例子:
var handler = new HttpClientHandler() { AllowAutoRedirect = true };
var ph = new ProgressMessageHandler(handler);
ph.HttpSendProgress += (_, args) =>
{
Console.WriteLine($"upload progress: {(double)args.BytesTransferred / args.TotalBytes}");
};
ph.HttpReceiveProgress += (_, args) =>
{
Console.WriteLine($"download progress: {(double)args.BytesTransferred / args.TotalBytes}");
};
var client = new HttpClient(ph);
await client.SendAsync(...);
Run Code Online (Sandbox Code Playgroud)
请注意,如果上传字节数组,这不会报告进度。请求消息内容必须是流。
以下代码显示了必须对HttpClient
api 执行哪些操作才能获取下载进度的最小示例。
HttpClient client = //...
// Must use ResponseHeadersRead to avoid buffering of the content
using (var response = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead)){
// You must use as stream to have control over buffering and number of bytes read/received
using (var stream = await response.Content.ReadAsStreamAsync())
{
// Read/process bytes from stream as appropriate
// Calculated by you based on how many bytes you have read. Likely incremented within a loop.
long bytesRecieved = //...
long? totalBytes = response.Content.Headers.ContentLength;
double? percentComplete = (double)bytesRecieved / totalBytes;
// Do what you want with `percentComplete`
}
}
Run Code Online (Sandbox Code Playgroud)
上面并没有告诉你如何处理流,如何报告进程,或者尝试为原始问题中的代码提供直接的解决方案。然而,对于希望在代码中取得进展的未来读者来说,这个答案可能更容易理解。
这是我对 Ren\xc3\xa9 Sackers 的答案的变体。主要区别:
\n public async static Task Download(\n string downloadUrl,\n string destinationFilePath,\n Func<long?, long, double?, bool> progressChanged)\n {\n using var httpClient = new HttpClient { Timeout = TimeSpan.FromDays(1) };\n using var response = await httpClient.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);\n\n response.EnsureSuccessStatusCode();\n var totalBytes = response.Content.Headers.ContentLength;\n\n using var contentStream = await response.Content.ReadAsStreamAsync();\n var totalBytesRead = 0L;\n var readCount = 0L;\n var buffer = new byte[8192];\n var isMoreToRead = true;\n\n static double? calculatePercentage(long? totalDownloadSize, long totalBytesRead) => totalDownloadSize.HasValue ? Math.Round((double)totalBytesRead / totalDownloadSize.Value * 100, 2) : null;\n\n using var fileStream = new FileStream(destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);\n\n do\n {\n var bytesRead = await contentStream.ReadAsync(buffer);\n if (bytesRead == 0)\n {\n isMoreToRead = false;\n\n if (progressChanged(totalBytes, totalBytesRead, calculatePercentage(totalBytes, totalBytesRead)))\n {\n throw new OperationCanceledException();\n }\n\n continue;\n }\n\n await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead));\n\n totalBytesRead += bytesRead;\n readCount++;\n\n if (readCount % 100 == 0)\n {\n if (progressChanged(totalBytes, totalBytesRead, calculatePercentage(totalBytes, totalBytesRead)))\n {\n throw new OperationCanceledException();\n }\n }\n }\n while (isMoreToRead);\n }\n
Run Code Online (Sandbox Code Playgroud)\n可以这样调用:
\n // Change this variable to stop the download\n // You can use a global variable or some kind of state management\n var mustStop = false;\n\n var downloadProgress = (long? _, long __, double? progressPercentage) =>\n {\n if (progressPercentage.HasValue)\n progressBar.Value = progressPercentage.Value;\n\n // In this example only the variable is checked\n // You could write other code that evaluates other conditions\n return mustStop;\n };\n\n SomeClass.Download("https://example.com/bigfile.zip", "c:\\downloads\\file.zip", downloadProgress);\n
Run Code Online (Sandbox Code Playgroud)\n
与上面的 @Ren\xc3\xa9 Sackers 解决方案相同,但添加了取消下载的功能
\n\nclass HttpClientDownloadWithProgress : IDisposable\n{\n private readonly string _downloadUrl;\n private readonly string _destinationFilePath;\n private readonly CancellationToken? _cancellationToken;\n\n private HttpClient _httpClient;\n\n public delegate void ProgressChangedHandler(long? totalFileSize, long totalBytesDownloaded, double? progressPercentage);\n\n public event ProgressChangedHandler ProgressChanged;\n\n public HttpClientDownloadWithProgress(string downloadUrl, string destinationFilePath, CancellationToken? cancellationToken = null)\n {\n _downloadUrl = downloadUrl;\n _destinationFilePath = destinationFilePath;\n _cancellationToken = cancellationToken;\n }\n\n public async Task StartDownload()\n {\n _httpClient = new HttpClient { Timeout = TimeSpan.FromDays(1) };\n\n using (var response = await _httpClient.GetAsync(_downloadUrl, HttpCompletionOption.ResponseHeadersRead))\n await DownloadFileFromHttpResponseMessage(response);\n }\n\n private async Task DownloadFileFromHttpResponseMessage(HttpResponseMessage response)\n {\n response.EnsureSuccessStatusCode();\n\n var totalBytes = response.Content.Headers.ContentLength;\n\n using (var contentStream = await response.Content.ReadAsStreamAsync())\n await ProcessContentStream(totalBytes, contentStream);\n }\n\n private async Task ProcessContentStream(long? totalDownloadSize, Stream contentStream)\n {\n var totalBytesRead = 0L;\n var readCount = 0L;\n var buffer = new byte[8192];\n var isMoreToRead = true;\n\n using (var fileStream = new FileStream(_destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))\n {\n do\n {\n int bytesRead;\n if (_cancellationToken.HasValue)\n {\n bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, _cancellationToken.Value);\n }\n else\n {\n bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);\n }\n\n if (bytesRead == 0)\n {\n isMoreToRead = false;\n continue;\n }\n\n await fileStream.WriteAsync(buffer, 0, bytesRead);\n\n totalBytesRead += bytesRead;\n readCount += 1;\n\n if (readCount % 10 == 0)\n TriggerProgressChanged(totalDownloadSize, totalBytesRead);\n }\n while (isMoreToRead);\n\n }\n\n //the last progress trigger should occur after the file handle has been released or you may get file locked error\n TriggerProgressChanged(totalDownloadSize, totalBytesRead);\n }\n\n private void TriggerProgressChanged(long? totalDownloadSize, long totalBytesRead)\n {\n if (ProgressChanged == null)\n return;\n\n double? progressPercentage = null;\n if (totalDownloadSize.HasValue)\n progressPercentage = Math.Round((double)totalBytesRead / totalDownloadSize.Value * 100, 2);\n\n ProgressChanged(totalDownloadSize, totalBytesRead, progressPercentage);\n }\n\n public void Dispose()\n {\n _httpClient?.Dispose();\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
Ren\xc3\xa9 Sackers 版本非常出色,但还可以更好。具体来说,它有一个微妙的竞争条件,这是由流关闭之前触发 TriggerProgressChanged 引起的。修复方法是在显式处理流后触发该事件。下面的版本包含上述更改,继承自 HttpClient 并添加了对取消令牌的支持。
\n\npublic delegate void ProgressChangedHandler(long? totalFileSize, long totalBytesDownloaded, double? progressPercentage);\n\npublic class HttpClientWithProgress : HttpClient\n{\n private readonly string _DownloadUrl;\n private readonly string _DestinationFilePath;\n\n public event ProgressChangedHandler ProgressChanged;\n\n public HttpClientWithProgress(string downloadUrl, string destinationFilePath)\n {\n _DownloadUrl = downloadUrl;\n _DestinationFilePath = destinationFilePath;\n }\n\n public async Task StartDownload()\n {\n using (var response = await GetAsync(_DownloadUrl, HttpCompletionOption.ResponseHeadersRead))\n await DownloadFileFromHttpResponseMessage(response);\n }\n\n public async Task StartDownload(CancellationToken cancellationToken)\n {\n using (var response = await GetAsync(_DownloadUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken))\n await DownloadFileFromHttpResponseMessage(response);\n }\n\n private async Task DownloadFileFromHttpResponseMessage(HttpResponseMessage response)\n {\n response.EnsureSuccessStatusCode();\n long? totalBytes = response.Content.Headers.ContentLength;\n using (var contentStream = await response.Content.ReadAsStreamAsync())\n await ProcessContentStream(totalBytes, contentStream);\n }\n\n private async Task ProcessContentStream(long? totalDownloadSize, Stream contentStream)\n {\n long totalBytesRead = 0L;\n long readCount = 0L;\n byte[] buffer = new byte[8192];\n bool isMoreToRead = true;\n\n using (FileStream fileStream = new FileStream(_DestinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))\n {\n do\n {\n int bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);\n if (bytesRead == 0)\n {\n isMoreToRead = false;\n continue;\n }\n\n await fileStream.WriteAsync(buffer, 0, bytesRead);\n\n totalBytesRead += bytesRead;\n readCount += 1;\n\n if (readCount % 10 == 0)\n TriggerProgressChanged(totalDownloadSize, totalBytesRead);\n }\n while (isMoreToRead);\n }\n TriggerProgressChanged(totalDownloadSize, totalBytesRead);\n }\n\n private void TriggerProgressChanged(long? totalDownloadSize, long totalBytesRead)\n {\n if (ProgressChanged == null)\n return;\n\n double? progressPercentage = null;\n if (totalDownloadSize.HasValue)\n progressPercentage = Math.Round((double)totalBytesRead / totalDownloadSize.Value * 100, 2);\n\n ProgressChanged(totalDownloadSize, totalBytesRead, progressPercentage);\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
归档时间: |
|
查看次数: |
27066 次 |
最近记录: |