在 ASP.NET Core 3.1 中上传和下载大文件?

Asp*_*ian 28 c# file-io large-file-upload asp.net-core asp.net-core-webapi

我正在使用干净的架构开发 ASP.NET Core 3.1 API 项目,并且我有以下类库(层):

  • 基础设施(安全的东西和上传助手等......)
  • 持久化(DA层)
  • 领域(领域模型)
  • 应用程序(用例 - 业务逻辑)
  • API(API项目作为我的启动项目)

我希望能够将大文件上传到服务器(例如 2Gb 的文件大小甚至更大)并在此之后下载它们,并且希望在将来不会出现内存溢出和所有问题的情况下进行下载

任何帮助,将不胜感激。

Com*_*Guy 61

如果你有这么大的文件,千万不要在你的代码中使用byte[]MemoryStream。如果您下载/上传文件,则仅对流进行操作。

你有几个选择:

  • 如果您同时控制客户端和服务器,请考虑使用tus 之类的东西。.NET 有客户端和服务器实现。这可能是最简单和最可靠的选择。
  • 如果您使用 HttpClient 上传大文件,只需使用StreamContent该类发送它们。同样,不要使用 aMemoryStream作为源,而是使用其他类似FileStream.
  • 如果您使用 HttpClient 下载大文件,则指定 HttpCompletionOptions 很重要,例如var response = await httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead). 否则,HttpClient 会将整个响应缓存在内存中。然后,您可以通过 将响应文件作为流处理var stream = response.Content.ReadAsStreamAsync()

ASP.NET Core 具体建议:

  • 如果要通过 HTTP POST 接收文件,则需要增加请求大小限制:[RequestSizeLimit(10L * 1024L * 1024L * 1024L)][RequestFormLimits(MultipartBodyLengthLimit = 10L * 1024L * 1024L * 1024L)]. 另外需要关闭表单值绑定,否则整个请求都会被缓存到内存中:
   [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
   public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
   {
       public void OnResourceExecuting(ResourceExecutingContext context)
       {
           var factories = context.ValueProviderFactories;
           factories.RemoveType<FormValueProviderFactory>();
           factories.RemoveType<FormFileValueProviderFactory>();
           factories.RemoveType<JQueryFormValueProviderFactory>();
       }

       public void OnResourceExecuted(ResourceExecutedContext context)
       {
       }
   }
Run Code Online (Sandbox Code Playgroud)
  • 要从控制器返回文件,只需通过File接受流的方法返回它:return File(stream, mimeType, fileName);

示例控制器如下所示(有关缺少的帮助程序类,请参阅https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1):

   [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
   public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
   {
       public void OnResourceExecuting(ResourceExecutingContext context)
       {
           var factories = context.ValueProviderFactories;
           factories.RemoveType<FormValueProviderFactory>();
           factories.RemoveType<FormFileValueProviderFactory>();
           factories.RemoveType<JQueryFormValueProviderFactory>();
       }

       public void OnResourceExecuted(ResourceExecutedContext context)
       {
       }
   }
Run Code Online (Sandbox Code Playgroud)

在此示例中,我们将整个文件流式传输到另一个服务。在某些情况下,最好将文件临时保存到磁盘。

  • 无需手动对文件进行分块。但是,请注意,如果您发送大文件并且出现网络错误,则需要重新发送整个文件(这就是 [tus](https://tus.io/) 试图解决的问题)。如果你对它们进行分块,你只需要发送失败的块,但那时,我会简单地使用 tus 而不是重新发明轮子。 (2认同)
  • tus 库仅处理上传文件。 (2认同)