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.

7
  • did you try changing controller method to public async Task Upload(ICollection<IFormFile> files) Commented May 21, 2017 at 15:20
  • But I am passing only one file in the service @Dipak Commented May 21, 2017 at 15:27
  • I also suspect that you posting collection as FormData. so change to List and give a go! Commented May 21, 2017 at 16:38
  • I tried, it dint work. Should I have to set anything in the controller to accept the mulitpart/form-data? Commented May 21, 2017 at 16:55
  • Could you check Request.Form.Files controller property from Upload action? Commented May 21, 2017 at 19:00

4 Answers 4

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();
}
Sign up to request clarification or add additional context in comments.

2 Comments

If i do not use IFormFile then Request.Form.Files is empty. I am using angular 7 and .netcore 2.1
Request.Form.Files was a life saviour! [FromForm] did not helped
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

7 Comments

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?
@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.
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.
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<IActionResult> UploadOtherLogo([FromForm(Name = "anotherFile")] IFormFile anotherFile)'?
I would presume so.yes :)
|
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!

1 Comment

This isn't working. Using Angular 7 and .NET Core 2.1.1
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');
    }
  });

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.