我正在开发一款UWP应用程序,它使用自定义API方法将文件上传到服务器.文件上传本身是正常的,但我在上传过程中执行进度跟踪时遇到了问题.我try 了几种方法,包括创建一个定制的ProgressStreamContent类,但似乎都不起作用

以下是没有跟踪进度的原始API方法:

public async Task<ApiResult<FileUploadResult>> UploadFile(string metadata, Stream content, string sessionId, string treeId, string nodeId,Action<long, long> progressCallback = null, CancellationToken? cancellationToken = null)
        {
            string url = $"{_httpHelper.GetFileUploadsBaseUrl()}/{sessionId}/{treeId}/{nodeId}";

            MultipartFormDataContent multipartContent = new MultipartFormDataContent
            {
                { new StringContent(metadata), "metadata" },
                { new StreamContent(content), "content" }

            };

            ApiResult<FileUploadResult> result = await _httpHelper.PostAsync<FileUploadResult>(url, multipartContent, cancellationToken);

            return result;

        }

下面是我使用定制的ProgressStreamContent类添加进度跟踪的try :

    public class ProgressStreamContent : HttpContent
    {


        private const int defaultBufferSize = 5 * 4096;

        private HttpContent content;
        private int bufferSize;
        //private bool contentConsumed;
        private Action<long, long> progress;

        public ProgressStreamContent(HttpContent content, Action<long, long> progress) : this(content, defaultBufferSize, progress) { }

        public ProgressStreamContent(HttpContent content, int bufferSize, Action<long, long> progress)
        {
            if (content == null)
            {
                throw new ArgumentNullException("content");
            }
            if (bufferSize <= 0)
            {
                throw new ArgumentOutOfRangeException("bufferSize");
            }

            this.content = content;
            this.bufferSize = bufferSize;
            this.progress = progress;

            foreach (var h in content.Headers)
            {
                this.Headers.Add(h.Key, h.Value);
            }
        }

        protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {

            return Task.Run(async () =>
            {
                var buffer = new Byte[this.bufferSize];
                long size;
                TryComputeLength(out size);
                var uploaded = 0;


                using (var sinput = await content.ReadAsStreamAsync())
                {
                    while (true)
                    {
                        var length = sinput.Read(buffer, 0, buffer.Length);
                        if (length <= 0) break;

         
                        uploaded += length;
                        progress?.Invoke(uploaded, size);

                        stream.Write(buffer, 0, length);
                        stream.Flush();
                    }
                }
                stream.Flush();
            });
        }

        protected override bool TryComputeLength(out long length)
        {
            length = content.Headers.ContentLength.GetValueOrDefault();
            return true;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                content.Dispose();
            }
            base.Dispose(disposing);
        }
}

下面是我如何使用它的,我创建了DelegatingHandler来处理进度

 public class ProgressMessageHandler : DelegatingHandler
    {
        private Action<long, long> _onUploadProgress;

        public event Action<long, long> HttpProgress
        {
            add => _onUploadProgress += value;
            remove => _onUploadProgress -= value;
        }
        public ProgressMessageHandler(HttpMessageHandler innerHandler) : base(innerHandler)
        {
        }
        protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var progressContent = new ProgressStreamContent(
                request.Content,
                4096,
                (sent, total) =>
                {
                    Console.WriteLine("Uploading {0}/{1}", sent, total);
                    OnUploadProgress(sent, total);
                });
            request.Content = progressContent;
            return await base.SendAsync(request, cancellationToken);
        }
        private void OnUploadProgress(long bytesTransferred, long totalBytes)
        {
            _onUploadProgress?.Invoke(bytesTransferred, totalBytes);
        }
    }

并将Api函数更新为

        public async Task<ApiResult<FileUploadResult>> UploadFile(string metadata, Stream content, string sessionId, string treeId, string nodeId,Action<long, long> progressCallback = null, CancellationToken? cancellationToken = null)
        {
            string url = $"{_httpHelper.GetFileUploadsBaseUrl()}/{sessionId}/{treeId}/{nodeId}";

            MultipartFormDataContent multipartContent = new MultipartFormDataContent
            {
                { new StringContent(metadata), "metadata" },
                { new ProgressStreamContent(new StreamContent(content), progressCallback), "content" }

            };

            ApiResult<FileUploadResult> result = await _httpHelper.PostAsync<FileUploadResult>(url, multipartContent, cancellationToken);

            return result;

        }

我正在寻求关于如何在我的UWP应用程序中正确执行文件上传过程中的进度跟踪的指导.任何见解或替代方法都将受到极大的赞赏.谢谢!

推荐答案

我认为你把这件事搞得太过火了.您可以使用ProgressStream from this answer,然后只需添加一个事件处理程序即可.

我已经对其进行了修改,添加了必要的async个版本.

public class ProgressStream : Stream
{
    private Stream _input;
    private long _progress;

    public event Action<long, long>? UpdateProgress;

    public ProgressStream(Stream input)
    {
        _input = input;
    }

    public override void Flush() => _input.Flush();

    public override Task FlushAsync(CancellationToken cancellationToken = default) => _input.FlushAsync(cancellationToken);

    public override int Read(Span<byte> buffer)
    {
        int n = _input.Read(buffer, offset, count);
        _progress += n;
        UpdateProgress?.Invoke(_progress, _input.Length);
        return n;
    }

    public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
    {
        int n = await _input.ReadAsync(buffer, cancellationToken);
        _progress += n;
        UpdateProgress?.Invoke(_progress, _input.Length);
        return n;
    }

    protected override void Dispose(bool disposing) => _input.Dispose();

    public override ValueTask DisposeAsync() => _input.DisposeAsync();

    public override void Write(byte[] buffer, int offset, int count) => throw new System.NotImplementedException();

    public override long Seek(long offset, SeekOrigin origin) => throw new System.NotImplementedException();

    public override void SetLength(long value) => throw new System.NotImplementedException();

    public override bool CanRead => true;
    public override bool CanSeek => false;
    public override bool CanWrite => false;
    public override long Length => _input.Length;
    public override long Position
    {
        get {  return _input.Position; }
        set {  throw new System.NotImplementedException();}
    }
}

然后连接事件处理程序,只需使用普通的StreamContent.

public async Task<ApiResult<FileUploadResult>> UploadFile(
  string metadata, Stream content, string sessionId, string treeId, string nodeId,
  Action<long, long> progressCallback = null, CancellationToken? cancellationToken = null)
{
    var url = $"{_httpHelper.GetFileUploadsBaseUrl()}/{sessionId}/{treeId}/{nodeId}";

    using var progressStream = progressCallback != null ? new ProgressStream(content) : content;
    // for performance use original stream if no callback
    if (progressCallback != null)
        progressStream.UpdateProgress += progressCallback;

    using var multipartContent = new MultipartFormDataContent
    {
        { new StringContent(metadata), "metadata" },
        { new StreamContent(progressStream), "content" },
    };

    var result = await _httpHelper.PostAsync<FileUploadResult>(url, multipartContent, cancellationToken);
    return result;
}

Csharp相关问答推荐

VS Code - C# - dotnet run找不到文件,但我可以打开并编辑它们吗?

在实际上是List T的 IESEARCH上多次调用First()是否不好?

EF Core:看不到任何查询日志(log)?

禁用AutoSuggestBox项目更改时的动画?

无法从具有一对多关系的C#类中使用Swagger创建记录

如何使用C#和Graph API从Azure Directory获取用户详细信息

应该使用哪一个?"_counter += 1 OR互锁增量(ref_counter)"""

有没有办法使.NET 6应用程序在特定的.NET 6运行时版本上运行

最新的Mediatr和具有同步方法的处理程序Handle:并非所有代码路径都返回值"

使用C#HttpClient以多部分形式数据发送带有非ASCII文件名的文件的问题

C#-从基类更新子类

从.Net 6 DLL注册和检索COM对象(Typelib导出:类型库未注册.(异常来自HRESULT:0x80131165))

对于PowerShell中的ConvertTo-SecureString方法,Microsoft如何将初始化向量添加到AES加密中的安全字符串?

.NET 6:如何防止系统生成的日志(log)?

在';、';附近有错误的语法.必须声明标量变量";@Checkin";.';

C#Microsoft.CodeAnalysis.CSharp.Scriiting不等待并行.对于

如何使用IHostedService添加数据种子方法

在C#/ASP.NET Core 7中,什么可能导致POST请求作为GET请求发送

如何为控制器PUT操作绑定对象数组

CsvHelper在第二次迭代时抛出System.ObjectDisposedException