【问题标题】:What is the best strategy to upload large file using HttpClient in a low memory windows phone device?在低内存 Windows 手机设备中使用 HttpClient 上传大文件的最佳策略是什么?
【发布时间】:2015-01-19 01:25:03
【问题描述】:

我正在尝试使用类似的方法上传文件HttpClient: How to upload multiple files at once 在 Windows 手机中。

using (var content = new MultipartFormDataContent())
{
    content.Add(CreateFileContent(imageStream, "image.jpg", "image/jpeg"));
    content.Add(CreateFileContent(signatureStream, "image.jpg.sig", "application/octet-stream"));

    var response = await httpClient.PostAsync(_profileImageUploadUri, content);
    response.EnsureSuccessStatusCode();
}

private StreamContent CreateFileContent(Stream stream, string fileName, string contentType)
{
    var fileContent = new StreamContent(stream);
    fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") 
    { 
        Name = "\"files\"", 
        FileName = "\"" + fileName + "\""
    }; // the extra quotes are key here
    fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);            
    return fileContent;
}

这在上传小文件时可以正常工作。如果我尝试在低端设备(512Mb 内存)中上传更大的文件(比如 > 50mb), 它抛出 System.OutOfMemoryException。我使用诊断工具来监控内存消耗并注意到内存 在 PostAsync 调用期间呈指数增长。似乎是将整个内容复制到内存中。现在我们在 api。

在低内存 Windows 手机设备中使用 HttpClient 上传大文件的最佳策略是什么?

【问题讨论】:

  • 这里有一个解决方案:stackoverflow.com/questions/26223902/…
  • Windows.Networking.BackgroundTransfer.BackgroundUploader 怎么样?看这里:stackoverflow.com/a/27430331/27211
  • 所有用于下载和上传的 API 调用都在 SDK 中实现(支持 .net 4.5.1、Windows Phone 8.1 和 Windows Store 8.1 的便携式类库)。据我所知,PCL 中不提供对 Windows.Networking.BackgroundTransfer.BackgroundUploader 的支持,如果我错了,请纠正。我必须使用 HttpClient 来实现它。

标签: c# .net windows-phone-8.1 httpclient


【解决方案1】:

手动执行多部分 POST - 无需 MultipartFormDataContent 的帮助

如果你必须多部分发送它,那么你可以发布更多manually,从源文件中读取 4k 缓冲区块。

您不一定需要使用异步方法这样做。解决方案是“手动控制 4k 缓冲”。但异步是最理想的,因为它是最高效的线程/CPU。

这是另一个推荐的链接来了解如何code multipart posts。还有一个是为了理解协议,下面是一个通过流发送的示例,illustrating the boundary markers

此外,在架构上,我倾向于将文件与任何(表单)数据分开上传。这完全避免了多部分发布,使您的 API 具有原子性和简单性。您可能有一个服务,它简单地存储上传的文件并返回 URL 或 ID。然后可以使用您的数据引用该 URL 或 ID 并随后发布。

【讨论】:

  • 我无法控制服务器端的 api。我正在尝试遵循第二种方法。也尝试使用strathweb.com/2013/01/…
  • 该链接与多部分无关,如果您从他们的异步架构开始,可能会混淆您的手动实施。您应该从我的链接开始,并从您的链接中学习应用异步方法。实现手动 4k 缓冲 POST 多部分流的方法有很多,但我的总体答案基本上是手动完成并放弃 MultipartFormDataContent。
  • @Todd 为什么不推荐使用 MultipartFormDataContent?
  • @gusmally-ApologisetoMonica- 我认为我的回答中包含了完整的原因。 1) 多部分增加了客户端和服务器的复杂性。复杂性会导致错误和更难维护的代码。 2)在可能的情况下,函数应该只有一个目的。 3) 在架构上,很容易将文件上传数据与表单数据分开。因此,除非是旧系统或较新的第三方系统的 API,否则没有充分的理由使用 multi-part。
【解决方案2】:

我并不是 MultipartFormDataContent 方面的专家(它可能会在水下拆分内容),但建议您拆分要发送的数据。

然后发送其他块并在接收端重构它。

例如将图像分成更小的块(例如 10mb 或更少,具体取决于内存使用情况)并发送这些

因此可能会导致 for 循环遍历这些块。

foreach (byte[] block in dividedContent)
{
    using (var content = new MultipartFormDataContent())
    {
        content.Add(block);

        var response = await httpClient.PostAsync(_profileImageUploadUri, content);
        response.EnsureSuccessStatusCode();
    }
}

也许这样的事情会解决你的问题:)

【讨论】:

  • 我对服务器端 api 没有任何控制权。我必须找到一种上传方式,直到 api 支持分块上传。
  • dividedContent,并像 7z multi archieve 一样将其连接在一起?好主意,但还没有尝试过
【解决方案3】:

除了 StreamContent 类之外,您还可以使用 fileStream:

    using (var fileStream = File.OpenRead(filepath))
    {
       var response = await _httpClient.PostAsync(requestUri, new 
          StreamContent(fileStream)).ConfigureAwait(false);
    }

这似乎完成了读取文件并在后台发布文件的所有必要分块,此外,您可以根据需要在 StreamContent 构造函数中设置缓冲区大小。即使文件很大,我也没有看到我的应用程序内存占用增加超过 1 MB。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-10
    • 1970-01-01
    • 2017-12-28
    • 2012-01-22
    • 2011-03-17
    • 2017-07-12
    • 2017-07-31
    • 2011-02-03
    相关资源
    最近更新 更多