在 ASP.NET Core 3.1 中上传和下载大文件? [英] Uploading and Downloading large files in ASP.NET Core 3.1?

查看:54
本文介绍了在 ASP.NET Core 3.1 中上传和下载大文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am working on an ASP.NET Core 3.1 API project using clean architecture and I have the following classlibs (tiers):

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

我希望能够将大文件上传到服务器(例如 2Gb 的文件大小甚至更大)并在此之后下载它们,并且希望这样做以后不会出现内存问题溢出和一切.

I want to be able to upload large files to server (like 2Gb of file size or even more) and download them after that and want to do it without having future problems with memory overflowing and everything.

任何帮助将不胜感激.

推荐答案

如果您的文件很大,切勿在代码中使用 byte[]MemoryStream.如果您下载/上传文件,则仅对流进行操作.

If you have files that large, never use byte[] or MemoryStream in your code. Only operate on streams if you download/upload files.

您有几个选择:

  • 如果您同时控制客户端和服务器,请考虑使用类似 tus 的内容..NET 有客户端和服务器实现.这可能是最简单、最可靠的选择.
  • 如果您使用 HttpClient 上传大文件,只需使用 StreamContent 类来发送它们.同样,不要使用 MemoryStream 作为源,而是使用其他类似 FileStream 的东西.
  • 如果您使用 HttpClient 下载大文件,请务必指定 HttpCompletionOptions,例如 var response = await httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead).否则,HttpClient 会将整个响应缓存在内存中.然后,您可以通过 var stream = response.Content.ReadAsStreamAsync() 将响应文件作为流处理.
  • If you control both client and server, consider using something like tus. There are both client- and server-implementations for .NET. This would probably the easiest and most robust option.
  • If you upload large files with the HttpClient, simply use the StreamContent class to send them. Again, don't use a MemoryStream as source, but something else like a FileStream.
  • If you download large files with the HttpClient, it is important to specify the HttpCompletionOptions, for example var response = await httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead). Otherwise, the HttpClient would buffer the entire response in memory. You can then process the response file as a stream via var stream = response.Content.ReadAsStreamAsync().

ASP.NET Core 具体建议:

ASP.NET Core specific advice:

  • 如果你想通过 HTTP POST 接收文件,你需要增加请求大小限制:[RequestSizeLimit(10L * 1024L * 1024L * 1024L)][RequestFormLimits(MultipartBodyLengthLimit =10L * 1024L * 1024L * 1024L)].另外需要关闭表单值绑定,否则整个请求都会被缓存到内存中:
  • If you want to receive files via HTTP POST, you need to increase the request size limit: [RequestSizeLimit(10L * 1024L * 1024L * 1024L)] and [RequestFormLimits(MultipartBodyLengthLimit = 10L * 1024L * 1024L * 1024L)]. In addition, you need to disable the form value binding, otherwise the whole request will be buffered into memory:
   [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)
       {
       }
   }

  • 要从控制器返回文件,只需通过 File 方法返回它,该方法接受一个流:return File(stream, mimeType, fileName);

    • To return a file from a controller, simple return it via the File method, which accepts a stream: return File(stream, mimeType, fileName);
    • 示例控制器如下所示(请参阅 https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1 用于缺少的帮助程序类):

      A sample controller would look like this (see https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1 for the missing helper classes):

      private const MaxFileSize = 10L * 1024L * 1024L * 1024L; // 10GB, adjust to your need
      
      [DisableFormValueModelBinding]
      [RequestSizeLimit(MaxFileSize)]
      [RequestFormLimits(MultipartBodyLengthLimit = MaxFileSize)]
      public async Task ReceiveFile()
      {
          if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
              throw new BadRequestException("Not a multipart request");
      
          var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType));
          var reader = new MultipartReader(boundary, Request.Body);
      
          // note: this is for a single file, you could also process multiple files
          var section = await reader.ReadNextSectionAsync();
      
          if (section == null)
              throw new BadRequestException("No sections in multipart defined");
      
          if (!ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition))
              throw new BadRequestException("No content disposition in multipart defined");
      
          var fileName = contentDisposition.FileNameStar.ToString();
          if (string.IsNullOrEmpty(fileName))
          {
              fileName = contentDisposition.FileName.ToString();
          }
      
          if (string.IsNullOrEmpty(fileName))
              throw new BadRequestException("No filename defined.");
      
          using var fileStream = section.Body;
          await SendFileSomewhere(fileStream);
      }
      
      // This should probably not be inside the controller class
      private async Task SendFileSomewhere(Stream stream)
      {
          using var request = new HttpRequestMessage()
          {
              Method = HttpMethod.Post,
              RequestUri = new Uri("YOUR_DESTINATION_URI"),
              Content = new StreamContent(stream),
          };
          using var response = await _httpClient.SendAsync(request);
          // TODO check response status etc.
      }
      
      

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

      In this example, we stream the entire file to another service. In some cases, it would be better to save the file temporarily to the disk.

      这篇关于在 ASP.NET Core 3.1 中上传和下载大文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆