c# - 使用 HttpClient 将大文件流传递给 MultipartFormDataContent
问题描述
尝试MultipartFormDataContent
与HttpClient
数据流一起使用时遇到问题。
语境
我正在尝试将一个大文件上传到 ASP.NET Core Web API。客户端应通过 POST 请求表单数据将文件发送到前端 API,而前端 API 又应将文件转发到后端 API。
因为文件可能很大,所以我按照微软的例子,即我不想使用IFormFile
type 而是阅读Request.Body
usingMultipartReader
。这是为了避免将整个文件加载到服务器的内存中,或者将其保存在服务器硬盘上的临时文件中。
问题
后端 API 控制器操作如下所示(这几乎是直接从 ASP.NET Core 5.0示例应用程序复制而来,只是做了一些小的简化):
[HttpPost]
[DisableRequestSizeLimit]
public async Task<IActionResult> ReceiveLargeFile()
{
var request = HttpContext.Request;
if (!request.HasFormContentType
|| !MediaTypeHeaderValue.TryParse(request.ContentType, out var mediaTypeHeader)
|| string.IsNullOrEmpty(mediaTypeHeader.Boundary.Value))
{
return new UnsupportedMediaTypeResult();
}
var reader = new MultipartReader(mediaTypeHeader.Boundary.Value, request.Body);
/* This throws an IOException: Unexpected end of Stream, the content may have already been read by another component. */
var section = await reader.ReadNextSectionAsync();
while (section != null)
{
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition,
out var contentDisposition);
if (hasContentDispositionHeader
&& contentDisposition!.DispositionType.Equals("form-data")
&& !string.IsNullOrEmpty(contentDisposition.FileName.Value))
{
/* Fake copy to nothing since it doesn't even get here */
await section.Body.CopyToAsync(Stream.Null);
return Ok();
}
section = await reader.ReadNextSectionAsync();
}
return BadRequest("No files data in the request.");
}
Microsoft.AspNetCore.Mvc.Testing
通过使用NuGet 包进行集成测试,我设法稍微减少了这个问题。以下测试替换了前端 API,因此Request.Body
该测试不是在 Web API 中读取流,而是尝试将其添加StreamContent
到后端 APIMultipartFormDataContent
并将其发布HttpClient
到后端 API:
[Fact]
public async Task Client_posting_to_Api_returns_Ok()
{
/* Arrange */
await using var stream = new MemoryStream();
await using var writer = new StreamWriter(stream);
await writer.WriteLineAsync("FILE CONTENTS");
await writer.FlushAsync();
stream.Position = 0;
using var client = _factory.CreateDefaultClient();
/* Act */
using var response =
await client.PostAsync(
"Receive",
new MultipartFormDataContent
{
{
new StreamContent(stream),
"file",
"fileName"
}
});
/* Assert */
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
后端 API 控制器然后抛出一个IOException
at await reader.ReadNextSectionAsync()
,说“意外的 Stream 结束,内容可能已经被另一个组件读取”。
GitHub 存储库(完整示例)
我上传了一个完整的问题示例(包括后端 API 和测试)一个GitHub repo。
问题
我一定做错了什么。如何在不将整个文件加载到前端的内存或硬盘驱动器的情况下,将在一个服务(前端 API)中的表单数据内容类型的请求中接收到的文件转发到另一个服务(后端 API) API,即只是将数据流转发到后端API?
提前感谢您的帮助。