HttpClient上传的进度条

时间:2014-01-15 06:17:55

标签: wpf upload progress-bar portable-class-library dotnet-httpclient

我想在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和进度条上传?这似乎是一个适度的需求。还是我应该使用另一个班级?非常感谢。

2 个答案:

答案 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
    }
}