我想在WPF中使用进度条运行异步上传(最好使用PCL在Xamarin和SL中重用代码。)
我一直在尝试使用System.Net.HttpClient。 不幸的是,PutAsync不提供任何进度通知。 (我知道它适用于Windows.Web.Http.HttpClient,但这不适用于WPF,也不适用于PCL)。
对于下载,它很容易实现自己的进度条,如here所述。您只需传递ResponseHeadersRead选项,该选项会在返回标题后立即使用该流,然后您可以在块中按块读取它,随时增加进度条。但是对于上传,这种技术不起作用 - 您需要一次性将所有上传数据传递给PutAsync,因此没有机会增加您的计数器。
我也想知道如何使用HttpClient.SendAsync。我希望我可以像对待异步HttpWebRequest一样对待它(在你写入HttpWebRequest.GetRequestStream时可以增加计数器,如here所述)。但遗憾的是HttpClient.SendAsync不会为您提供可写流,因此无效。
HttpClient是否支持使用非阻止UI和进度条上传?这似乎是一个适度的需求。还是我应该使用另一个班级?非常感谢。
答案 0 :(得分:4)
假设HttpClient(和底层网络堆栈)没有缓冲,您应该可以通过覆盖HttpContent.SerializeToStreamAsync来完成此操作。您可以执行以下操作:
const int chunkSize = 4096;
readonly byte[] bytes;
readonly Action<double> progress;
protected override async Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext context)
{
for (int i = 0; i < this.bytes.Length; i += chunkSize)
{
await stream.WriteAsync(this.bytes, i, Math.Min(chunkSize, this.bytes.Length - i));
this.progress(100.0 * i / this.bytes.Length);
}
}
为了避免被HttpClient缓冲,您需要提供内容长度(例如:实现HttpContent.TryComputeLength,或设置标头)或启用HttpRequestHeaders.TransferEncodingChunked。这是必要的,因为否则HttpClient无法确定内容长度标头,因此它首先将整个内容读入内存。
在手机8上你还需要禁用AllowAutoRedirect,因为WP8在处理重定向的帖子时有一个错误(解决方法是首先发出HEAD,获取重定向的URL,然后使用AllowAutoRedirect =将帖子发送到最终的URL)假)。
答案 1 :(得分:0)
上传带有进度的文件的简单方法
我有同样的需求,经过一些尝试后发现,您可以通过跟踪要上传的文件的 FileStream 的 Position
轻松获得字节精确的上传进度。
这是一种方法...
FileStream fileToUpload = File.OpenRead(@"C:\test.mp3");
HttpContent content = new StreamContent(fileToUpload);
HttpRequestMessage msg = new HttpRequestMessage{
Content=content,
RequestUri = new Uri(--yourUploadURL--)
}
bool keepTracking = true; //to keep tracking thread running
new Task(new Action(() => { progressTracker(fileToUpload, ref keepTracking); })).Start();
var result = httpClient.SendAsync(msg).Result;
keepTracking = false; //to stop the tracking thread
函数progressTracker()
定义为,
void progressTracker(FileStream streamToTrack, ref bool keepTracking)
{
int prevPos = -1;
while (keepTracking)
{
int pos = (int)Math.Round(100 * (streamToTrack.Position / (double)streamToTrack.Length));
if (pos != prevPos)
{
Console.WriteLine(pos + "%");
}
prevPos = pos;
Thread.Sleep(100); //only update progress every 100ms
}
}