【问题标题】:HttpClient Stream Upload not working with queryparamsHttpClient Stream Upload 不适用于查询参数
【发布时间】:2021-06-04 08:56:12
【问题描述】:

我想通过流将文件上传到我的 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();
        }

    }
}

所以如果我删除查询一切正常。我无法对自己解释这一点,因为查询不应该打扰到这一点,对吧?

【问题讨论】:

  • 您使用的是哪个 ASP.net Core 版本?可能和这个有关:github.com/dotnet/aspnetcore/issues/10503
  • @FranciscoTena 我正在使用 ASP.Net Core 3.1。不幸的是,这对我没有帮助
  • 什么是ThrottledStream?当我不得不处理非常大的文件时,我使用了可恢复的上传/下载库(如 Tus)
  • @FranciscoTena ThrottledStream 是用于限制上传速度的自定义流,但您也可以使用普通 FileStream。结果是一样的。
  • 你可以尝试使用 var userName = new StringContent("test");内容。添加(用户名,“用户名”);而不是传入网址?

标签: c# asp.net-core dotnet-httpclient


【解决方案1】:

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

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

  • 多部分/表单数据
  • 多部分/相关
  • 具有 Application/json 内容类型的 Base64 编码文件
  • 将正文请求与 MIME 类型一起使用

RFC multipart specification 也很有趣,因为您可以了解如何定义请求中的boundaries。我认为处理此要求的最佳方法是使用允许您使用resume filesretry 的库。

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

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

下一步是告诉控制器使用与 [FromForm] IFormFileCollection 文件命名方式相同的参数的内容:

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

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

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

也就是说,这只是一种方法(既不是最好的也不是最差的),只是一种快速解决这种情况的方法。更好的办法是考虑拥有代表整个表单(请求中包含的数据)的 DTO,并处理提到的Microsoft blog post 解释的安全问题,其中:

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

【讨论】:

  • 非常感谢您对我的帮助和工作!!
猜你喜欢
  • 2020-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多