HttpClient 在上传之前读取整个文件。 UWP

Posted

技术标签:

【中文标题】HttpClient 在上传之前读取整个文件。 UWP【英文标题】:HttpClient reading entire file before upload. UWP 【发布时间】:2017-03-03 22:51:19 【问题描述】:

我正在制作一个将文件上传到 facebook 的 UWP 应用,我正在使用自定义 HttpContent 以 4k 块的形式上传文件,以最大限度地减少大文件 (>100mb) 的内存使用并报告进度。

我的自定义 HttpContent UploadWithProgressHttpContent:

 class UploadWithProgressHttpContent : HttpContent

    private readonly IProgress<OperationProgress> _progress;
    private readonly OperationProgress _data;
    private readonly Stream _file;
    private readonly int _bufferSize;
    private readonly CancellationToken _token;

    public UploadWithProgressHttpContent(
        IProgress<OperationProgress> progress,
    OperationProgress data,
    Stream file,
    int bufferSize,
    CancellationToken token)

    _progress = progress;
    _data = data;
    _file = file;
    _bufferSize = bufferSize;
    _token = token;




protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)

    return CopyStreamWithProgress(_file, stream, _progress, _token, _data, _bufferSize);


public static async Task<Stream> CopyStreamWithProgress(
    Stream source,
    Stream destination,
    IProgress<OperationProgress> progress,
    CancellationToken token,
    OperationProgress progressData,
    int bufferSize
    )

    int read, offset = 0;
    var buffer = new byte[bufferSize];
    using (source)
    
        do
        
            read = await source.ReadAsync(buffer, 0, bufferSize, token);

            await destination.WriteAsync(buffer, 0, read, token);

            offset += read;
            progressData.CurrentSize = offset;
            progress.Report(progressData);
         while (read != 0);
    

    return destination;


我遇到的(使用提琴手)是整个文件在上传开始之前被放入内存中(我的进度表在上传开始之前就达到了 100%)。

我确实尝试将TransferEncodingChunked 设置为true,并设置文件内容长度,但问题仍然存在。

上传源位于 PCL 内(如果重要)。我正在使用最新版本的System.Net.Http。如果需要,我使用的方式与MediaFire SDK中使用的方式完全相同

感谢您的帮助。

编辑:添加了 HttpClient 用法:

public async Task<T> Upload<T>(Stream fileStream, string fileName)

    var handler = new HttpClientHandler();


    var cli = new HttpClient(handler);

    foreach (var header in Headers)
    
        cli.DefaultRequestHeaders.Add(header.Key, header.Value);
    

    var parameters = new MultipartFormDataContent();
    foreach (var parameter in Parameters)
    
        parameters.Add(new StringContent(parameter.Value), parameter.Key);
    

    if (fileStream != null)
    
        var fileContent = new UploadWithProgressHttpContent(ProgressOperation, ProgressData, fileStream,
            _chunkBufferSize, Token, fileStream.Length);

        fileContent.Headers.ContentType = new MediaTypeHeaderValue(MimeTypeHelper.GetMimeType(fileName));
        fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue(StreamParamName);
        fileContent.Headers.ContentDisposition.FileName = fileName;
        fileContent.Headers.ContentLength = fileStream.Length;
        parameters.Add(fileContent, StreamParamName);
    

    var req = new HttpRequestMessage(method, Path)  Content = parameters ;
    if (fileStream != null)
        req.Headers.TransferEncodingChunked = true;


    var completionOption = HttpCompletionOption.ResponseContentRead;


    var resp = await cli.SendAsync(req, completionOption, Token).ConfigureAwait(false);

    return await DeserializeObject<T>(resp);

【问题讨论】:

我无法使用自定义 HttpContent 解决此问题,我的替代方法是使用 StreamContent,并制作报告读取进度的流装饰器。通过使用 StreamContent HttpClient 流式传输内容而不将其(全部)带到内存中。 不完全是,我使用的是自定义 httpcontent,我已经检查了使用流内容是否可以按预期工作,我只是想知道为什么它不适用于自定义 httpcontent,或者如果有什么办法。 你能分享一下 HttpClient 周围的代码吗?顺便提一句。我会考虑改用wire Shark(不确定添加fiddler作为http代理是否不会影响流量)。 完成,感谢您的帮助! 您是否尝试过覆盖 HttpContent.TryComputeLength? 【参考方案1】:

你和量子力学有同样的问题——观察的行为会改变被观察的东西。 Fiddler 不支持请求流 - 请参阅 Fiddler makes HttpWebRequest/HttpClient behaviour unexpected 和 http://www.telerik.com/forums/is-it-possible-to-not-buffer-requests

使用wireshark我可以看到块。

【讨论】:

不完全,如果我禁用 fiddler,问题仍然存在。【参考方案2】:

在这个问题上浪费了几个小时后,这似乎是一个 HttpClient 实现问题。 因此,如果您希望将内容流式传输到服务器(并报告进度),最好的选择是使用 StreamContent 并装饰读取以报告进度。

注意:System.Net.Http 的 4.3.1 版本和 Microsoft.Net.Http 掘金包的 2.2.29 版本也是如此

【讨论】:

以上是关于HttpClient 在上传之前读取整个文件。 UWP的主要内容,如果未能解决你的问题,请参考以下文章

如何读取上传文件中的整个文件路径[重复]

使用 C# .NET HTTPClient 在 Node JS 服务器上通过 Multer 将 Revit 文件上传到 Autodesk Forge OSS

童鞋,[HttpClient发送文件的技术实践]请查收

HttpClient在响应流完全读取之前被释放,导致连接关闭异常

在内容 100% 完成之前从 HttpResponseMessage 读取标头

U盘移动硬盘等弹出 “文件或目录损坏且无法读取” 实测解决办法