6
votes

When looking at other answers and some google, everything seems to be fine, yet my controller never receives any data.

Api uris and such are correct, the request arrives at the correct controller

Angular snippet:

component.html - my input field

<div class="input-group">
    <input type="file" #fileInput id="fileInput" (change)="stageFile()"
           accept="csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel">
    <div class="input-group-append">
        <button class="btn btn-primary" type="button" (click)="fileUpload()" [disabled]="!staged || uploading">
          <span *ngIf="!uploading,else loadAnim">Upload</span>
          <ng-template #loadAnim>Uploading...</ng-template>
        </button>
    </div>
</div>

component.ts - get data from view

@ViewChild('fileInput') fileInput;
private file: File;
public uploading = false;
public staged = false;

constructor(private uploadService: UploadService) { }

public stageFile(): void {
    this.staged = true;
    this.file = this.fileInput.nativeElement.files[0];
    console.log(this.file)
}

public fileUpload():void {
    this.uploading = true;
    if (this.file != null)
      this.uploadService.upload(this.file).subscribe();
    this.staged = false;
    this.uploading = false;
}

services.ts - handle actual ajax call

private uploadURI = environment.dataServiceURI + '/upload';

constructor(private http: HttpClient) {}

public upload(file: File): Observable<object> {
// create multipart form for file
let formData: FormData = new FormData();
formData.append('file', file, file.name);

const headers = new HttpHeaders().append('Content-Type', 'mulipart/form-data');

// POST
return this.http
  .post(this.uploadURI, formData, {headers: headers})
  .pipe(map(response => response));
}

.net core snippet

It is here where IFormFile file always contains null and thus my result is always 500

[HttpPost("upload")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult ParseData([FromForm(Name = "file")] IFormFile file)
{
    if (file == null)
       return StatusCode(500);
    (...)
    return Ok()
 }

Request Payload Info

From browser networking

------WebKitFormBoundarycBigaNKzS4qNcTBg 
Content-Disposition: form-data; name="file"; filename="test_data_schema.xlsx" 
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

------WebKitFormBoundarycBigaNKzS4qNcTBg--
2
When you look at the network tab of your browser, does it send your file? Just to check if the problem comes from your frontend or your backend...hugo
yes it seems to send the file, at least the request payload contains a Content-Disposition and a Content-Type with the correct form-data values. Even if the payload would be empty I wouldn't know why. Any way to inspect if it actually sends a file and not just a filename? Logging the file to the console before sending it give me a size in bytes, but that's all I got.pjominet
There is a typo in your code: const headers = new HttpHeaders().append('Content-Type', 'mulipart/form-data'); --> "mulipart"hugo
I check against my dotnet code: public Task<IActionResult> Backup(IFormFile file) Maybe you should remove FromForm option because it's convention based ('file' in the payload will match 'file' in the parameters)hugo
Fund my error (thanks to @hugo) i took a closer look at my headers and there wasn't just a typo, but a mistake too. It has to be Content-Disposition not Content-Typepjominet

2 Answers

8
votes

Problem solved:

in service.ts it should be

const headers = new HttpHeaders().append('Content-Disposition', 'multipart/form-data');

instead of

const headers = new HttpHeaders().append('Content-Type', 'multipart/form-data');

Also in the .net controller, adding or removing [FromForm(Name = "file")] as parameter prefix does not change the behavior. It works perfectly fine with or without it. As hugo pointed out in the comments it's convention based as long as the param name matches to name in the form data.

1
votes

Agree, the Content-Disposition header did help. One more condition should be met: the first parameter of formData.append(....) method: the first parameter of

formData.append(...)

method:

enter image description here should be the same with the name of agrument in controller action:

enter image description here