0

我正在寻找有关如何从面向外部的 Angular 应用程序到面向外部的 Asp 执行非缓冲大文件(千兆字节+)上传的建议。Net 5.0 Web API 端点,然后到面向内部的 Asp。网络 Web API 端点。使用ASP.Net Core 中上传文件的指导,我从 Angular 到面向外部的 API 的第一跳工作正常,但这仅提供了一半的解决方案。我能够在不消耗太多内存的情况下将 2-gig 文件上传到第一个端点(我看到消耗了大约 300MB,这有点令人担忧,但我稍后会处理)。当我尝试流式传输到内部端点时,我得到了Unexpected end of Stream,内容可能已被另一个组件读取。当我尝试处理流并写入磁盘时。

本质上,我正在寻找一种让 DMZ 充当某种传递或代理的方法,以便我可以将文件安全地流式传输并存储到内部文件存储中,而不会耗尽服务器内存资源。

下面是我的 Angular 到 DMZ 端点流程的 C#、html 和 Typescript——我不知道从那里去哪里。关于如何在不消耗太多内存或 CPU 的情况下完成这项工作的任何建议?

我的客户端代码目前非常简单

HTML

<div class="row" style="margin-bottom:15px;">
    <div class="col-md-3">
      <input type="file" #file placeholder="Choose file" 
             (change)="uploadFile(file.files)"
             style="display:none;" multiple>
        <button type="button" class="btn btn-success" 
                (click)="file.click()">Upload File</button>
    </div>
    <div class="col-md-4">
        <span class="upload" *ngIf="progress > 0">
            {{ progress }}%
        </span>
        <span class="upload" *ngIf="message">
            {{ message }}
        </span>
    </div>
</div>

打字稿

public uploadFile = (files) => {
  if (files.length === 0) {
    return;
  }

  let filesToUpload : File[] = files;
  const formData = new FormData();
  
  Array.from(filesToUpload).map((file, index) => {
    return formData.append('file'+index, file, file.name);
  });

  this.http.post('https://localhost:5001/api/upload/uploadFileToInternalAPI', formData, 
        {reportProgress: true, observe: 'events'})
    .subscribe(event => {
      if (event.type === HttpEventType.UploadProgress)
        this.progress = Math.round(100 * event.loaded / event.total);
      else if (event.type === HttpEventType.Response) {
        this.message = 'Upload success.';
        this.onUploadFinished.emit(event.body);
      }
    });
}

C#

这是我的 DMZ 端点 C# 代码。注意我试图在一个请求中支持多个文件。

[HttpPost("uploadFileToInternalAPI")]
[DisableFormValueModelBinding] // Passing no parameters to the method essentially does the same thing as this attribute
[RequestSizeLimit(MaxFileSize)]
[RequestFormLimits(MultipartBodyLengthLimit = MaxFileSize)]
public async Task<IActionResult> UploadFileToInternalAPI()
{

    var request = HttpContext.Request;

    // validation of Content-Type
    if (!request.HasFormContentType ||
        !MediaTypeHeaderValue.TryParse(request.ContentType, out var mediaTypeHeader) ||
        string.IsNullOrEmpty(mediaTypeHeader.Boundary.Value))
    {
        return new UnsupportedMediaTypeResult();
    }

    // Setup to get the first section (file) from the request
    var reader = new MultipartReader(mediaTypeHeader.Boundary.Value, request.Body);
    var section = await reader.ReadNextSectionAsync();

    // Setup MultipartFormDataContent to post to internal API
    var forwardingContent = new MultipartFormDataContent();

    // Loop and process each section (file) in the multipart request. 
    // Builds up a multi-part request to forward to the internal API
    while (section != null)
    {
        var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition
               out var contentDisposition);
        if (hasContentDispositionHeader && contentDisposition.DispositionType.Equals("form-data") &&
            !string.IsNullOrEmpty(contentDisposition.FileName.Value))
        {
            // Get the filename from the section
            var fileName = contentDisposition.FileName.Value.Trim('"');

            // Stream content allows to pass a non-buffered stream
            forwardingContent.Add(new StreamContent(section.Body), "file", fileName);
        }

        section = await reader.ReadNextSectionAsync();    
    }

    // For example only -- Don't create a HttpClient like this in production
    var client = new HttpClient { BaseAddress = new Uri("http://localhost:5002") };
    client.DefaultRequestHeaders.Accept.Clear();

    // *** Here's where things break down - I'm not sure how to 
    //     forward the request onto the backend without consuming
    //     the stream content.
    var response = await client.PostAsync("/internalUpload/upload", forwardingContent);
    if (response.IsSuccessStatusCode)
    {
        return Ok();
    }

    // If the code runs to this location, it means that no files have been saved
    return BadRequest("No files data in the request.");
}

内部 API C#

我的内部 API 本质上是相同的 C#,但不是发布到端点,而是将流写入磁盘文件共享。

4

0 回答 0