C# Stream.CopyToAsync with progress reporting-即使在复制完成后也会报告进度
我构建了一个简单的控制台应用程序,可以从internet下载文件。C# Stream.CopyToAsync with progress reporting-即使在复制完成后也会报告进度,c#,asynchronous,dotnet-httpclient,.net-4.6,C#,Asynchronous,Dotnet Httpclient,.net 4.6,我构建了一个简单的控制台应用程序,可以从internet下载文件。 因为我决定使用HttpClient编写我的应用程序 基本上,我是在请求读取标题,然后使用ReadAsStreamAsync获取流,并使用CopyToAsync将其复制到本地文件 我找到了支持IProgress的流扩展方法: public static class StreamExtensions { public static async Task CopyToAsync(this Stream source, Stre
因为我决定使用HttpClient编写我的应用程序 基本上,我是在请求读取标题,然后使用
ReadAsStreamAsync
获取流,并使用CopyToAsync
将其复制到本地文件
我找到了支持IProgress的流扩展方法:
public static class StreamExtensions
{
public static async Task CopyToAsync(this Stream source, Stream destination, IProgress<long> progress, CancellationToken cancellationToken = default(CancellationToken), int bufferSize = 0x1000)
{
var buffer = new byte[bufferSize];
int bytesRead;
long totalRead = 0;
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
{
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
totalRead += bytesRead;
//Thread.Sleep(10);
progress.Report(totalRead);
}
}
}
正如您所看到的,我得到了文件2已下载的信息,但我仍然从CopyToAsync
获取进度报告,与文件1相同
因此,我有时会得到这种奇怪的控制台输出:
理想情况下,我希望比打电话时更确定:
await streamToReadFrom.CopyToAsync(streamToWriteTo, progress, source.Token,0x2000);
Debug.WriteLine(filename+" downloaded");
在获得调试信息后,不会报告任何进度(文件已下载)。我原以为等待可以解决我的问题,但事实并非如此
我怎样才能解决这个问题?作为一个临时解决方案,我在报告进度之前将Thread.Sleep添加到CopyToAsync
以下是我目前的代码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncDownloadTest
{
class Program
{
private const string LocalPath = @"D:\TEMP";
static void Main()
{
try
{
var filesToDownlad = new List<Tuple<string, string>>
{
new Tuple<string, string>("file1.tmp", "http://ipv4.download.thinkbroadband.com/10MB.zip"),
new Tuple<string, string>("file2.tmp", "http://ipv4.download.thinkbroadband.com/10MB.zip")
};
_consolePosition = -1;
Console.CursorVisible = false;
Parallel.ForEach(filesToDownlad, new ParallelOptions { MaxDegreeOfParallelism = 4 }, doc =>
{
DownloadFile(doc.Item2,doc.Item1).Wait();
});
Debug.WriteLine("ALL FILES DOWNLOADED");
Console.CursorVisible = true;
}
catch (Exception e)
{
Console.WriteLine(e);
Console.ReadLine();
}
}
private static readonly object ConsoleLock = new object();
private static int _consolePosition;
static readonly CancellationTokenSource source = new CancellationTokenSource();
private static async Task DownloadFile(string url, string filename)
{
int currenctLineNumber = 0;
int currectProgress = 0;
try
{
lock (ConsoleLock)
{
_consolePosition++;
currenctLineNumber = _consolePosition;
}
long fileSize = -1;
IProgress<long> progress = new Progress<long>(value =>
{
decimal tmp = (decimal)(value * 100) / fileSize;
if (tmp != currectProgress && tmp > currectProgress)
{
lock (ConsoleLock)
{
currectProgress = (int)tmp;
Console.CursorTop = currenctLineNumber;
Console.CursorLeft = 0;
Console.Write("{0,10} - {2,11} - {1,6:N2}%", filename, tmp, "DOWNLOADING");
}
Debug.WriteLine("{1} {0:N2}%", tmp, filename);
}
});
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, source.Token))
{
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength.HasValue) fileSize = response.Content.Headers.ContentLength.Value;
if (response.Content.Headers.ContentDisposition != null)
{
var tmp = response.Content.Headers.ContentDisposition.FileName.Replace("\"", "");
Debug.WriteLine("Real name: {0}",tmp);
}
using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync())
{
using (Stream streamToWriteTo = File.Open(Path.Combine(LocalPath, filename), FileMode.Create, FileAccess.Write))
{
await streamToReadFrom.CopyToAsync(streamToWriteTo, progress, source.Token,0x2000);
Debug.WriteLine(filename+" downloaded");
lock (ConsoleLock)
{
Console.CursorTop = currenctLineNumber;
Console.CursorLeft = 0;
var oldColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("{0,10} - {2,11} - {1,6:N2}%", filename, 100, "SUCCESS");
Console.ForegroundColor = oldColor;
}
}
}
}
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
lock (ConsoleLock)
{
Console.CursorTop = currenctLineNumber;
Console.CursorLeft = 0;
var oldColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("{0,10} - {2,11} - {1,6:N2}%", filename, currectProgress, "ERROR");
Console.ForegroundColor = oldColor;
}
}
}
}
public static class StreamExtensions
{
public static async Task CopyToAsync(this Stream source, Stream destination, IProgress<long> progress, CancellationToken cancellationToken = default(CancellationToken), int bufferSize = 0x1000)
{
var buffer = new byte[bufferSize];
int bytesRead;
long totalRead = 0;
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
{
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
totalRead += bytesRead;
Thread.Sleep(10);
progress.Report(totalRead);
}
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.IO;
使用System.Linq;
使用System.Net.Http;
使用系统线程;
使用System.Threading.Tasks;
命名空间异步下载测试
{
班级计划
{
私有常量字符串LocalPath=@“D:\TEMP”;
静态void Main()
{
尝试
{
var filesToDownlad=新列表
{
新元组(“file1.tmp”http://ipv4.download.thinkbroadband.com/10MB.zip"),
新元组(“file2.tmp”http://ipv4.download.thinkbroadband.com/10MB.zip")
};
_合并位置=-1;
Console.CursorVisible=false;
ForEach(filesToDownlad,新的ParallelOptions{maxdegreeofpparallelism=4},doc=>
{
下载文件(doc.Item2,doc.Item1).Wait();
});
Debug.WriteLine(“下载的所有文件”);
Console.CursorVisible=true;
}
捕获(例外e)
{
控制台写入线(e);
Console.ReadLine();
}
}
私有静态只读对象控制台洛克=新对象();
专用静态int_控制台位置;
静态只读CancellationTokenSource=新CancellationTokenSource();
私有静态异步任务下载文件(字符串url、字符串文件名)
{
int currenctLineNumber=0;
int currenctprogress=0;
尝试
{
锁(控制台锁)
{
_consolePosition++;
currenctLineNumber=_consolePosition;
}
长文件大小=-1;
i进度=新进度(值=>
{
十进制tmp=(十进制)(值*100)/文件大小;
如果(tmp!=当前进度&&tmp>当前进度)
{
锁(控制台锁)
{
当前进度=(int)tmp;
Console.CursorTop=currenctLineNumber;
Console.CursorLeft=0;
Write(“{0,10}-{2,11}-{1,6:N2}%”,文件名,tmp,“下载”);
}
WriteLine(“{1}{0:N2}%”,tmp,文件名);
}
});
使用(HttpClient=new HttpClient())
{
使用(httpresponsemessageresponse=wait client.GetAsync(url,HttpCompletionOption.ResponseHeadersRead,source.Token))
{
response.EnsureSuccessStatusCode();
if(response.Content.Headers.ContentLength.HasValue)fileSize=response.Content.Headers.ContentLength.Value;
if(response.Content.Headers.ContentDisposition!=null)
{
var tmp=response.Content.Headers.ContentDisposition.FileName.Replace(“\”,”);
WriteLine(“实名:{0}”,tmp);
}
使用(Stream streamToReadFrom=wait response.Content.ReadAsStreamAsync())
{
使用(Stream streamToWriteTo=File.Open(Path.Combine(LocalPath,filename)、FileMode.Create、FileAccess.Write))
{
等待streamToReadFrom.CopyToAsync(streamToWriteTo,progress,source.Token,0x2000);
Debug.WriteLine(文件名+“已下载”);
锁(控制台锁)
{
Console.CursorTop=currenctLineNumber;
Console.CursorLeft=0;
var oldColor=Console.ForegroundColor;
Console.ForegroundColor=ConsoleColor.Green;
Write(“{0,10}-{2,11}-{1,6:N2}%”,文件名,100,“SUCCESS”);
Console.ForegroundColor=oldColor;
}
}
}
}
}
}
捕获(例外e)
{
Debug.WriteLine(e.Message);
锁(控制台锁)
{
Console.CursorTop=currenctLineNumber;
Console.CursorLeft=0;
var oldColor=Console.ForegroundColor;
Console.ForegroundColor=ConsoleColor.Red;
Write(“{0,10}-{2,11}-{1,6:N2}%”,文件名,cur
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncDownloadTest
{
class Program
{
private const string LocalPath = @"D:\TEMP";
static void Main()
{
try
{
var filesToDownlad = new List<Tuple<string, string>>
{
new Tuple<string, string>("file1.tmp", "http://ipv4.download.thinkbroadband.com/10MB.zip"),
new Tuple<string, string>("file2.tmp", "http://ipv4.download.thinkbroadband.com/10MB.zip")
};
_consolePosition = -1;
Console.CursorVisible = false;
Parallel.ForEach(filesToDownlad, new ParallelOptions { MaxDegreeOfParallelism = 4 }, doc =>
{
DownloadFile(doc.Item2,doc.Item1).Wait();
});
Debug.WriteLine("ALL FILES DOWNLOADED");
Console.CursorVisible = true;
}
catch (Exception e)
{
Console.WriteLine(e);
Console.ReadLine();
}
}
private static readonly object ConsoleLock = new object();
private static int _consolePosition;
static readonly CancellationTokenSource source = new CancellationTokenSource();
private static async Task DownloadFile(string url, string filename)
{
int currenctLineNumber = 0;
int currectProgress = 0;
try
{
lock (ConsoleLock)
{
_consolePosition++;
currenctLineNumber = _consolePosition;
}
long fileSize = -1;
IProgress<long> progress = new Progress<long>(value =>
{
decimal tmp = (decimal)(value * 100) / fileSize;
if (tmp != currectProgress && tmp > currectProgress)
{
lock (ConsoleLock)
{
currectProgress = (int)tmp;
Console.CursorTop = currenctLineNumber;
Console.CursorLeft = 0;
Console.Write("{0,10} - {2,11} - {1,6:N2}%", filename, tmp, "DOWNLOADING");
}
Debug.WriteLine("{1} {0:N2}%", tmp, filename);
}
});
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, source.Token))
{
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength.HasValue) fileSize = response.Content.Headers.ContentLength.Value;
if (response.Content.Headers.ContentDisposition != null)
{
var tmp = response.Content.Headers.ContentDisposition.FileName.Replace("\"", "");
Debug.WriteLine("Real name: {0}",tmp);
}
using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync())
{
using (Stream streamToWriteTo = File.Open(Path.Combine(LocalPath, filename), FileMode.Create, FileAccess.Write))
{
await streamToReadFrom.CopyToAsync(streamToWriteTo, progress, source.Token,0x2000);
Debug.WriteLine(filename+" downloaded");
lock (ConsoleLock)
{
Console.CursorTop = currenctLineNumber;
Console.CursorLeft = 0;
var oldColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("{0,10} - {2,11} - {1,6:N2}%", filename, 100, "SUCCESS");
Console.ForegroundColor = oldColor;
}
}
}
}
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
lock (ConsoleLock)
{
Console.CursorTop = currenctLineNumber;
Console.CursorLeft = 0;
var oldColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("{0,10} - {2,11} - {1,6:N2}%", filename, currectProgress, "ERROR");
Console.ForegroundColor = oldColor;
}
}
}
}
public static class StreamExtensions
{
public static async Task CopyToAsync(this Stream source, Stream destination, IProgress<long> progress, CancellationToken cancellationToken = default(CancellationToken), int bufferSize = 0x1000)
{
var buffer = new byte[bufferSize];
int bytesRead;
long totalRead = 0;
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
{
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
totalRead += bytesRead;
Thread.Sleep(10);
progress.Report(totalRead);
}
}
}
}
new Progress<long>
//C#6.0
public sealed class SynchronousProgress<T> : IProgress<T>
{
private readonly Action<T> _callback;
public SynchronousProgress(Action<T> callback) { _callback = callback; }
void IProgress<T>.Report(T data) => _callback(data);
}
//older version
public sealed class SynchronousProgress<T> : IProgress<T>
{
private readonly Action<T> _callback;
public SynchronousProgress(Action<T> callback)
{
_callback = callback;
}
void IProgress<T>.Report(T data)
{
_callback(data);
}
}
IProgress<long> progress = new Progress<long>(value =>
IProgress<long> progress = new SynchronousProgress<long>(value =>
static void Main()
{
try
{
var filesToDownlad = new List<Tuple<string, string>>
{
new Tuple<string, string>("file1.tmp", "http://ipv4.download.thinkbroadband.com/10MB.zip"),
new Tuple<string, string>("file2.tmp", "http://ipv4.download.thinkbroadband.com/10MB.zip")
};
_consolePosition = -1;
Console.CursorVisible = false;
var downloadBlock = new ActionBlock<Tuple<string, string>>(doc => DownloadFile(doc.Item2, doc.Item1),
new ExecutionDataflowBlockOptions {MaxDegreeOfParallelism = 4});
foreach (var file in filesToDownlad)
{
downloadBlock.Post(file);
}
downloadBlock.Complete();
downloadBlock.Completion.Wait();
Debug.WriteLine("ALL FILES DOWNLOADED");
Console.CursorVisible = true;
}
catch (Exception e)
{
Console.WriteLine(e);
Console.ReadLine();
}
}
static async Task Example()
{
try
{
var filesToDownlad = new List<Tuple<string, string>>
{
new Tuple<string, string>("file1.tmp", "http://ipv4.download.thinkbroadband.com/10MB.zip"),
new Tuple<string, string>("file2.tmp", "http://ipv4.download.thinkbroadband.com/10MB.zip")
};
_consolePosition = -1;
Console.CursorVisible = false;
var downloadBlock = new ActionBlock<Tuple<string, string>>(doc => DownloadFile(doc.Item2, doc.Item1),
new ExecutionDataflowBlockOptions {MaxDegreeOfParallelism = 4});
foreach (var file in filesToDownlad)
{
await downloadBlock.SendAsync(file);
}
downloadBlock.Complete();
await downloadBlock.Completion;
Debug.WriteLine("ALL FILES DOWNLOADED");
Console.CursorVisible = true;
}
catch (Exception e)
{
Console.WriteLine(e);
Console.ReadLine();
}
}