8

I am implememting file upload using Angular 2 with ASP.NET core Web API to handle the request.

My html code looks like :

<input #fileInput type="file"/>
<button (click)="addFile()">Add</button>

and the angular2 code

addFile(): void {
    let fi = this.fileInput.nativeElement;
    if (fi.files && fi.files[0]) {
        let fileToUpload = fi.files[0];
        this.documentService
            .uploadFile(fileToUpload)
            .subscribe(res => {
                console.log(res);
        });
    }
}

and the service looks like

public uploadFile(file: any): Observable<any> {
    let input = new FormData();
    input.append("file", file, file.name);
    let headers = new Headers();
    headers.append('Content-Type', 'multipart/form-data');
    let options = new RequestOptions({ headers: headers });
    return this.http.post(`/api/document/Upload`, input, options);
}

and the controller code

[HttpPost]
public async Task Upload(IFormFile file)
{
    if (file == null) throw new Exception("File is null");
    if (file.Length == 0) throw new Exception("File is empty");

    using (Stream stream = file.OpenReadStream())
    {
        using (var binaryReader = new BinaryReader(stream))
        {
            var fileContent = binaryReader.ReadBytes((int)file.Length);
            //await this.UploadFile(file.ContentDisposition);
        }
    }
}

My RequestHeader looks like

POST /shell/api/document/Upload HTTP/1.1
Host: localhost:10050
Connection: keep-alive
Content-Length: 2
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJDb3JyZWxhdGlvbklkIjoiZDZlNzE0OTUtZTM2MS00YTkxLWExNWUtNTc5ODY5NjhjNDkxIiwiVXNlcklkIjoiMSIsIlVzZXJOYW1lIjoiWjk5OTkiLCJXb3Jrc3BhY2UiOiJRc3lzVFRAU09BVEVNUCIsIk1hbmRhbnRJZCI6IjUwMDEiLCJDb3N0Q2VudGVySWQiOiIxMDAxIiwiTGFuZ3VhZ2VDb2RlIjoiMSIsIkxhbmd1YWdlU3RyaW5nIjoiZGUtREUiLCJTdGF0aW9uSWQiOiI1NTAwMSIsIk5hbWUiOiJJQlMtU0VSVklDRSIsImlzcyI6InNlbGYiLCJhdWQiOiJodHRwOi8vd3d3LmV4YW1wbGUuY29tIiwiZXhwIjoxNDk1Mzc4Nzg4LCJuYmYiOjE0OTUzNzUxODh9.5ZP7YkEJ2GcWX9ce-kLaWJ79P4d2iCgePKLqMaCe-4A
Origin: http://localhost:10050
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Content-Type: multipart/form-data
Accept: application/json, text/plain, */*
Referer: http://localhost:10050/fmea/1064001/content
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8

The issue I am facing is that the file is always null in the controller.

Please some one help me in figuring out the issue.

Thanks in advance.

Acjb
  • 505
  • 1
  • 6
  • 21

4 Answers4

20

You don't need to use 'multipart/form-data' with FormData

In Angular 2 component:

<input type="file" class="form-control" name="documents" (change)="onFileChange($event)" />

onFileChange(event: any) {
    let fi = event.srcElement;
    if (fi.files && fi.files[0]) {
        let fileToUpload = fi.files[0];

        let formData:FormData = new FormData();
         formData.append(fileToUpload.name, fileToUpload);

        let headers = new Headers();
        headers.append('Accept', 'application/json');
        // DON'T SET THE Content-Type to multipart/form-data, You'll get the Missing content-type boundary error
        let options = new RequestOptions({ headers: headers });

        this.http.post(this.baseUrl + "upload/", formData, options)
                 .subscribe(r => console.log(r));
    }
}

On API side

[HttpPost("upload")]
public async Task<IActionResult> Upload()
{
    var files = Request.Form.Files;

    foreach (var file in files)
    {
       // to do save
    }

    return Ok();
}
dipak
  • 2,011
  • 2
  • 17
  • 24
6

Update: After some clarification from the ASP.NET Core Team, it has to do with the compat switch in the Startup class. If you set it like this:

services
    .AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

then you are fine if you also remove the [FromForm] Attribute from the file parameter.

Old Post: I ran into a similiar problem with the latest ASP.NET Core WebApi (2.1.2 at the time of this post), which I could only solve by accident after an hour of stackoverflowing, research and lots of trial and error. I was posting the file from an Angular 6 application like this:

const formData: FormData = new FormData();
formData.append('file', file, file.name);

const req = new HttpRequest('POST', 'upload-url', formData, {
    reportProgress: true
});

this.http.request(req).subscribe(...) // omitted the rest

The problem was, that the IFormFile file Action method parameter was always null even when putting [FromForm] in front it. The [FromForm] was necessary due to api controller behavior changes in ASP.NET Core 2.1, where [FromBody] becomes the default for api controllers. Strangely, it still didn't work, the value stayed null.

I finally solved it by explicitly stating the name of the form content parameter using the attribute, like this:

public async Task<IActionResult> UploadLogo([FromForm(Name = "file")] IFormFile file)        
{
     ...
}

Now the file upload was bound correctly to the controller parameter. I hope this might help someone in the future as it almost cost me my sanity :D

Schmaga
  • 81
  • 2
  • 5
  • I'm not quite following the '[FromForm(Name = "file")]'? What is the "file" referring to? I'm trying to pass a ViewModel with a couple of strings and a File from Angular to ASP.NET Core, and the strings get passed fine, but the File is always null. Any ideas? – Evan Sevy Jan 10 '19 at 21:57
  • @RuneStar the "file" refers to the identifier of the file data in the posted form data. as it was appended in the Angular part of the code. Concerning your ViewModel question, I think the following answer has the right idea in that you probably need custom model binding for that. – Schmaga Jan 15 '19 at 16:10
  • The problem I was having was that the form didn't like it when I added the 'formControlName' or '[formControl]' directives on the 'File' input. Not sure why exactly, but got rid of that and I'm able to upload with the image to the controller action just fine. – Evan Sevy Jan 16 '19 at 00:04
  • As far as your reply, I'm curious, does that mean I can have a separate action for each of the any number of various 'FormData' identifiers and they will all be called upon each POST? For instance if you added a 'formData.append('anotherFile', file, file.anotherName)' I could create an action 'public async Task UploadOtherLogo([FromForm(Name = "anotherFile")] IFormFile anotherFile)'? – Evan Sevy Jan 16 '19 at 00:09
  • I would presume so.yes :) – Schmaga Jan 17 '19 at 21:18
  • +500 for you if I could! `[FromForm(Name = "file")] IFormFile file` did the trick for me – Reed Mar 21 '19 at 16:21
  • Thank you Mr.Schmaga It's killed lots of time.. And finally I found solution by using this precious one. – Rejwanul Reja May 21 '19 at 19:46
3

Another way with dotnet core, you can use IFormFile interface, with setting default headers:

Angular 2

 let formData = new FormData();
 formData.append("file", yourUploadFile);

 this.http.post("your_api_path", formData).subscribe(r => console.log(r));

Dotnet Core

 [HttpPost]
 [Route("/your_api_path")]
 public async Task<IActionResult> Upload(IFormFile file) {
     //...next awaiters...    
}

If you want send multiple files, you can use ICollection<IFormFile> as Upload params.

For sending multiple properties you can use custom model object and IFormFile will be one of properties.

Hope it help!

Kahbazi
  • 14,331
  • 3
  • 45
  • 76
Petr Tomášek
  • 1,428
  • 16
  • 24
2

Core 3.1

[HttpPost("upload-bulk-excel"), DisableRequestSizeLimit]
public IActionResult Upload()
{
  try
  {
    var file = Request.Form.Files[0];

Angular 8

uploadExcelFile() {
  const formData = new FormData();
  formData.append('file', this.fileToUpload, this.fileToUpload.name);

  this._httpClient.post(`${environment.apiUrl}/upload/upload-bulk-excel`, formData, {reportProgress: true, observe: 'events'})
    .subscribe(event => {
      if (event.type === HttpEventType.UploadProgress)
        this.uploadProgress = Math.round(100 * event.loaded / event.total);
      else if (event.type === HttpEventType.Response) {
        this.uploadMessage = 'Upload success.';
      alert('File uploaded successfully');
    }
  });

}

Dean
  • 731
  • 8
  • 21