HttpClient 流上传不适用于查询参数

HttpClient Stream Upload not working with queryparams

我想通过流将文件上传到我的 ASP.NET 核心 API 服务器,一切正常,直到我将查询字符串添加到我的 POST,在这种情况下我不没有出现异常,但文件是在服务器上创建的,但没有字节流入。我正在使用 Streams,因为我有非常大的文件 (20mb-8gb)

ASP.NET API:

[Route("api/[controller]")]
[ApiController]
public class UploadTestController : ControllerBase
{
    
    // POST api/<UploadTestController>
    [DisableRequestSizeLimit]
    [HttpPost]
    public async Task<IActionResult> Post([FromQuery]string username)
    {

        using (Stream stream = Request.Body)
        {
            try
            {
                using (var fstream = System.IO.File.OpenWrite(@"F:\TMP\file..."))
                {
                    await stream.CopyToAsync(fstream);
                }
                return Ok("test");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return BadRequest();
            }
        }
          
    }

    
}

客户:

class Program
{
    static void Main(string[] args)
    {
        Upload().Wait();
    }
    public static async Task Upload()
    {
        FileStream stream = new FileStream(@"C:\TEMP\file...", FileMode.Open);
        ThrottledStream throttledStream = new ThrottledStream(stream,900000);
        using (var client = new HttpClient())
        {
            client.Timeout = TimeSpan.FromMinutes(10);
            var content = new MultipartFormDataContent();
            content.Add(new StreamContent(throttledStream), "file...");
            try
            {
                HttpResponseMessage response =
                await client.PostAsync("https://localhost:5001/api/uploadtest?username=test", content);
                var cont = await response.Content.ReadAsStringAsync();
                Console.WriteLine(cont);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            

            
            
            Console.ReadKey();
        }

    }
}

因此,如果我删除查询,一切正常。我无法向自己解释,因为查询不应该打扰它,对吗?

请考虑处理大文件(假设大于数百 MB)涉及一些安全和可用性问题(恢复、溢出等)。在这方面有趣的阅读是 this.

另一个考虑因素是 .Net 如何处理多部分请求以及上传文件的不同选项:

  • Multipart/form-data
  • Multipart/related
  • 具有 Application/json 内容类型
  • 的 Base64 编码文件
  • 使用 MIME 类型的正文请求

还有RFC multipart specification is very interesting because you can learn how the boundaries in a request are defined. I think the best way to handle this requirements is by using libraries that allow you to resume files or retry.

在您的情况下,最简单的解决方案是将变量作为多部分内容的一部分发送,而不是在 url 请求中进行硬编码,并为流内容命名,这样您就可以让“ WebApi 背后的 magic" 正确地映射了这一点:

 content.Add(new StreamContent(stream), name: "files", "file...");
 var userName = new StringContent("test");
 content.Add(userName, "username");

下一步是告诉控制器使用具有以相同方式命名的参数的内容[FromForm] IFormFileCollection files:

public async Task<IActionResult> Upload([FromForm] IFormFileCollection files)

然后你可以像这样读取所有其他参数:

IFormCollection form = await HttpContext.Request.ReadFormAsync();
form.TryGetValue("username", out StringValues user);

也就是说,这只是一种方法(既不是最好的也不是最坏的),只是对这种情况的快速修复。最好考虑让 DTO 代表整个表单(请求中包含的数据),并处理上述 Microsoft blog post 中解释的安全问题,其中:

  • 配置框架限制
  • 禁用自动绑定
  • 处理取消
  • 进度报告