How to upload data + multiple files from Angular t

2020-04-15 20:51发布

问题:

My server uses .Net Core 2.1.402

Here is my C# class:

public class SampleDetailsDto
{
    public Guid Id{ get; set; }
    public string Text { get; set; }
    public IEnumerable<string> ImageUrls { get; set; }
    public IFormCollection Images { get; set; }
}

Here is my C# Controller

[HttpPut]
[Route("{id:guid}")]
public async Task<IActionResult> UpdateAsync([FromRoute]Guid id, [FromForm] SampleDetailsDtodto)
{
    Console.WriteLine(dto.Text); 
    Console.WriteLine(dto.Images.Length);
    return OK();
}

I use nswag to generate the client service, but there is currently a bug (https://github.com/RSuter/NSwag/issues/1421#issuecomment-424480418) with upload multiple files, so I extended the update method to create mine, here is the code:

 public update(id: string, dto: SampleDetailsDto | null | undefined): Observable<SampleDetailsDto | null> {
    let url_ = this._baseUrl + "/api/v1/Sample/{id}";
    if (id === undefined || id === null)
      throw new Error("The parameter 'id' must be defined.");
    url_ = url_.replace("{id}", encodeURIComponent("" + id));
    url_ = url_.replace(/[?&]$/, "");

    let options_: any = {
      observe: "response",
      responseType: "blob",
      headers: new HttpHeaders({
        "Accept": "application/json"
      })
    };
    return _observableFrom(this.transformOptions(options_)).pipe(_observableMergeMap(transformedOptions_ => {
      return this._http.put<SampleDetailsDto>(url_,dto, transformedOptions_);
    })).pipe(_observableMergeMap((response_: any) => {
      return this.transformResult(url_, response_, (r) => this.processUpdate(<any>r));
    })).pipe(_observableCatch((response_: any) => {
      if (response_ instanceof HttpResponseBase) {
        try {
          return this.transformResult(url_, response_, (r) => this.processUpdate(<any>r));
        } catch (e) {
          return <Observable<SampleDetailsDto | null>><any>_observableThrow(e);
        }
      } else
        return <Observable<SampleDetailsDto | null>><any>_observableThrow(response_);
    }));
  }

I would like to upload multiple files and data at the same time, in this case all the images are linked to SampleDetailsDto. But we can imagine have this case kind of case too:

public class SampleDetailsDto
{
    public Guid Id{ get; set; }
    public string Text { get; set; }
    public IEnumerable<ChildSampleDetailsDto> Children{ get; set; }
}

public class ChildSampleDetailsDto
{
    public Guid Id{ get; set; }
    public string Text { get; set; }
    public IEnumerable<string> ImageUrls { get; set; }
    public IFormCollection Images { get; set; }
}

Is it possible to send Data + multiple files to a .net Core Web Api?

Thanks

回答1:

Use IFormFile and [FromForm] and do not access the request to extract files.

Angular code:

public sendFiles(files: File[], [...]): Observable<any> {
   const formData = new FormData();
   formData.append('files', files); // add all the other properties
   return this.http.post('http://somehost/someendpoint/fileupload/', formData);
}

ASP.NET Core code:

public class MyFileUploadClass
{
   public IFormFile[] Files { get; set; }
   // other properties
}

[HttpPost("fileupload")]
public async Task<IActionResult> FileUpload([FromForm] MyFileUploadClass @class)  // -> property name must same as formdata key
{
   // do the magic here
   return NoContent();
}


回答2:

Read answer fron maherjendoubi here : https://forums.asp.net/t/2099194.aspx?Net+Core+Web+API+How+to+upload+multi+part+form+data

public ActionResult CreateDocument()
{
      foreach(var key in Request.Form.Keys)
        {
            var data = JsonConvert.DeserializeObject<ChildSampleDetailsDto>(Request.Form[key]);
            var file = Request.Form.Files["file" + key];

        }
        return Ok();
    }

For the angular part use an HttpRequest:

const sendable = new FormData();

for (let i; i < files.length; i++) {
    sendable.append('filedata' + i, files[i], files[i].name);
    sendable.append('data' + i, JSON.stringify(data[i]));
}
const request = new HttpRequest(item.method,
      item.url,
      sendable,
      {
        reportProgress: true
      });
// this._http: HttpClient
this._http.request(request)
      .subscribe((event: any) => {
        if (event.type === HttpEventType.UploadProgress) {
          // on progress code
        }
        if (event.type === HttpEventType.Response) {
          // on response code
        }
      }, error => {
        // on error code
      });