如何使用 C# HttpClient PostAsync 显示上传进度
Posted
技术标签:
【中文标题】如何使用 C# HttpClient PostAsync 显示上传进度【英文标题】:How to display upload progress using C# HttpClient PostAsync 【发布时间】:2016-02-10 16:11:11 【问题描述】:我正在使用 Xamarin PCL 创建适用于 android 和 ios 的文件上传应用程序,并且我已设法实现文件上传和某种进度条,但它无法正常工作。
我看到了一些关于显示下载进度的堆栈溢出的答案,但我想通知我的用户有关上传进度但没有找到任何解决方案。
这是我的代码:
public static async Task<string> PostFileAsync (Stream filestream, string filename, int filesize)
var progress = new System.Net.Http.Handlers.ProgressMessageHandler ();
//Progress tracking
progress.HttpSendProgress += (object sender, System.Net.Http.Handlers.HttpProgressEventArgs e) =>
int progressPercentage = (int)(e.BytesTransferred*100/filesize);
//Raise an event that is used to update the UI
UploadProgressMade(sender, new System.Net.Http.Handlers.HttpProgressEventArgs(progressPercentage, null, e.BytesTransferred, null));
;
using (var client = HttpClientFactory.Create(progress))
using (var content = new MultipartFormDataContent ("------" + DateTime.Now.Ticks.ToString ("x")))
content.Add (new StreamContent (filestream), "Filedata", filename);
using (var message = await client.PostAsync ("http://MyUrl.example", content))
var result = await message.Content.ReadAsStringAsync ();
System.Diagnostics.Debug.WriteLine ("Upload done");
return result;
显示了某种进度,但是当进度达到 100% 时,文件还没有上传。在我收到最后一条进度消息后的一段时间内,也会打印消息“上传完成”。
也许进度是显示从设备发出的字节,而不是尚未上传的字节,所以当它说,它是 100% 时,所有字节都刚刚发出,但还没有被服务器接收?
编辑:尝试了这个解决方案:https://forums.xamarin.com/discussion/56716/plans-to-add-webclient-to-pcl,效果更好。
【问题讨论】:
【参考方案1】:试试这样的:
我遇到了同样的问题。我通过实现自定义HttpContent
来修复它。我使用这个对象来跟踪上传进度的百分比,你可以添加一个事件并监听它。您应该自定义SerializeToStreamAsync
方法。
internal class ProgressableStreamContent : HttpContent
private const int defaultBufferSize = 4096;
private Stream content;
private int bufferSize;
private bool contentConsumed;
private Download downloader;
public ProgressableStreamContent(Stream content, Download downloader) : this(content, defaultBufferSize, downloader)
public ProgressableStreamContent(Stream content, int bufferSize, Download downloader)
if(content == null)
throw new ArgumentNullException("content");
if(bufferSize <= 0)
throw new ArgumentOutOfRangeException("bufferSize");
this.content = content;
this.bufferSize = bufferSize;
this.downloader = downloader;
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
Contract.Assert(stream != null);
PrepareContent();
return Task.Run(() =>
var buffer = new Byte[this.bufferSize];
var size = content.Length;
var uploaded = 0;
downloader.ChangeState(DownloadState.PendingUpload);
using(content) while(true)
var length = content.Read(buffer, 0, buffer.Length);
if(length <= 0) break;
downloader.Uploaded = uploaded += length;
stream.Write(buffer, 0, length);
downloader.ChangeState(DownloadState.Uploading);
downloader.ChangeState(DownloadState.PendingResponse);
);
protected override bool TryComputeLength(out long length)
length = content.Length;
return true;
protected override void Dispose(bool disposing)
if(disposing)
content.Dispose();
base.Dispose(disposing);
private void PrepareContent()
if(contentConsumed)
// If the content needs to be written to a target stream a 2nd time, then the stream must support
// seeking (e.g. a FileStream), otherwise the stream can't be copied a second time to a target
// stream (e.g. a NetworkStream).
if(content.CanSeek)
content.Position = 0;
else
throw new InvalidOperationException("SR.net_http_content_stream_already_read");
contentConsumed = true;
参考:
https://github.com/paulcbetts/ModernHttpClient/issues/80 Progress bar for HttpClient uploading https://forums.xamarin.com/discussion/18649/best-practice-to-upload-image-selected-to-a-web-api【讨论】:
你有完整的 sn-p 示例吗? ***.com/questions/22528839/… 这太棒了。谢谢!【参考方案2】:上传进度文件的最简单方法
您可以通过跟踪您要上传的文件的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 start and stop the tracking thread
new Task(new Action(() => progressTracker(fileToUpload, ref keepTracking); )).Start();
var result = httpClient.SendAsync(msg).Result;
keepTracking = false; //stops 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); //update every 100ms
【讨论】:
+1 确实非常简单!如果httpClient.SendAsync(msg).Result
抛出异常(例如,由于网络错误导致上传失败),只需一个额外的细节,那么keepTracking
将永远不会设置为false
,progressTracker()
函数将永远循环!【参考方案3】:
这是因为你做错了数学。
更改:int progressPercentage = (int)(e.BytesTransferred*100/filesize);
收件人:int progressPercentage = (int)(e.BytesTransferred/filesize) *100;
改用此代码:
double bytesOut = double.Parse(e.BytesTransferred.ToString());
double totalBytes = double.Parse(filesize.ToString());
double percentage = bytesOut / totalBytes * 100;
或者你可以简单地使用e.ProgressPercentage
【讨论】:
不,这不是问题所在。这是因为数据被缓冲并且只在最后发送。我最终使用了HttpWebRequest
,将SendChunked
设置为true,AllowWriteStreamBuffering
设置为false,然后在请求流中手动写入数据。
@MaximV.Pavlov 因为这是一个老问题,我不记得我是如何解决这个问题的,但从我最初问题的编辑来看,我想我可能使用了Xamarin 论坛链接。以上是关于如何使用 C# HttpClient PostAsync 显示上传进度的主要内容,如果未能解决你的问题,请参考以下文章
如何在 C# 中使用 HttpClient 发送文件和表单数据
如何在 C# 中使用 HttpClient 读取 Web api 响应
如何在 C# 中将 HttpClient PostAsync() 与线程池一起使用?
如何使用 C# HttpClient PostAsync 显示上传进度