3

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

Cedric Arnould
  • 1,991
  • 4
  • 29
  • 49

2 Answers2

6

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 be the same used as formdata key
{
   // do the magic here
   return NoContent();
}
Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
alsami
  • 8,996
  • 3
  • 25
  • 36
  • 1
    Hi, thanks for your help, but It doesnt work on my side, I created a bug on .net core which explaine more the issue: https://github.com/dotnet/core/issues/1967 Maybe you know a way to patch the pb? – Cedric Arnould Sep 27 '18 at 16:29
  • Then it must be your request. For me it works exactly that way. – alsami Sep 28 '18 at 07:27
  • Posted a simple in your github issue. – alsami Sep 28 '18 at 07:59
  • 1
    I just spend about an hour trying to get it work, just to find out my parameter name was not spelled correctly and didn't match the Angular name. Thanks for this! – Gladen Jul 26 '19 at 13:04
  • 1
    Been there! Glad I was able to help! – alsami Jul 26 '19 at 19:54
0

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
      });
agua from mars
  • 16,428
  • 4
  • 61
  • 70
  • Hi @agua from mars, Thanks for your help, but in your answer, you are uploading only one File. In my situation in would like to send multiple files. Moreover, in the second case written with ChildSampleDetailsDto, how to know to which Child the File belongs? – Cedric Arnould Sep 26 '18 at 18:16
  • 1
    You can add as many files as you want (`sendable.append('file' + i, files[i], files[i].name)`), and it's up to you to reference your file in your data. – agua from mars Sep 26 '18 at 18:27
  • You can for sample use the key of your data to reference it : `sendable.append('fileData' + i, JSON.stringify(datas[i]));` – agua from mars Sep 26 '18 at 18:35
  • So in my Case, it works with SampleDetailsDto, but when I want to add ChildSampleDetailsDto, i dont succeed, the Array is always empty (but not nul). For File, this solutions works, I can receive the data server side: content_.append('images[]', files[i], files[i].name); but not this one content_.append('images'+i, files[i], files[i].name); – Cedric Arnould Sep 26 '18 at 18:59
  • Did you check the content-type? And the request body? – agua from mars Sep 26 '18 at 19:15
  • By using this function : https://stackoverflow.com/a/49388446/6123422 It works partially.The uploaded files are not linked to a child or parent but linked to all. I verify the content type and request body. It seems not possible to upload files for a child. – Cedric Arnould Sep 26 '18 at 19:53
  • But you can change your model to meet your requirement right ? – agua from mars Sep 26 '18 at 20:13
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/180834/discussion-between-cedric-arnould-and-agua-from-mars). – Cedric Arnould Sep 26 '18 at 20:18