如何使用C#HttpClient PostAsync显示上传进度

Art*_*nis 6 c# file-upload progress httpclient xamarin

我正在使用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;
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

显示某种进度,但当进度达到100%时,文件尚未上载.在收到最后一条进度消息后的某个时间也会打印消息"上传已完成".

也许进展是显示从设备发出的字节而不是已经上传的字节,所以当它说是100%时,所有字节都只是发出但服务器尚未收到?

编辑:试过这个解决方案:https : //forums.xamarin.com/discussion/56716/plans-to-add-webclient-to-pcl ,它的效果更好一点.

Vai*_*esh 8

尝试这样的事情:

我遇到了同样的问题.我通过实现自定义来修复它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;
    }
}
Run Code Online (Sandbox Code Playgroud)

参考:


Abr*_*ham 8

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

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

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

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
Run Code Online (Sandbox Code Playgroud)

函数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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • +1确实非常简单!只是一个额外的细节,如果 `httpClient.SendAsync(msg).Result` 抛出异常(例如,由于网络错误导致上传失败),那么 `keepTracking` 将永远不会设置为 `false` 并且 `progressTracker()` 函数将永远循环! (2认同)
  • 在 Blazor 中不起作用,因为文件流位置立即达到 100%。 (2认同)

Ahm*_*aba -2

这是因为你的数学计算错了。

改变 :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;
Run Code Online (Sandbox Code Playgroud)

或者你可以简单地使用e.ProgressPercentage