【问题标题】:How to display upload progress using C# HttpClient PostAsync如何使用 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,效果更好。

【问题讨论】:

    标签: c# file-upload xamarin httpclient progress


    【解决方案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;
        }
    }
    

    参考:

    【讨论】:

    【解决方案2】:

    上传进度文件的最简单方法

    您可以通过跟踪您要上传的文件的FileStreamPosition 来获得准确的进度。

    这演示了如何做到这一点。

    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 将永远不会设置为falseprogressTracker() 函数将永远循环!
    【解决方案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 论坛链接。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-05
    • 1970-01-01
    • 2014-02-03
    • 1970-01-01
    • 1970-01-01
    • 2018-04-11
    • 1970-01-01
    相关资源
    最近更新 更多