title: How to Upload File from Angular to ASP.NET Core Web API
body: <p>Similar questions have been asked but after looking through all of those and many blog posts on the subject I have not been able to figure this out so please forgive me.</p>
  
  <p>I am creating a simple blog with (for the purposes of this question) two parts, a front end SPA in Angular 8 and a back-end API in ASP.NET Core 3. In one part of my front end I am attempting to upload an image to be used as the image for a newly created blog. When I try to upload an image, the resulting IFormFile in the backend is always coming out to <code>null</code>. Below is the code, any help is greatly appreciated!</p>
  
  <p>new-blog.component.html:</p>
  
  <pre class="lang-html prettyprint-override"><code>&lt;form [formGroup]="newBlogForm" (ngSubmit)="onSubmit(newBlogForm.value)"&gt;
  
      &lt;div&gt;
          &lt;label for="Name"&gt;
              Blog Name
          &lt;/label&gt;
          &lt;input type="text" formControlName="Name"&gt;
      &lt;/div&gt;
  
      &lt;div&gt;
          &lt;label for="TileImage"&gt;
              Tile Image
          &lt;/label&gt;
          &lt;input type="file" formControlName="TileImage"&gt;
      &lt;/div&gt;
  
      &lt;button type="submit"&gt;Create Blog&lt;/button&gt;
  
  &lt;/form&gt;
  </code></pre>
  
  <p>new-blog.component.ts:</p>
  
  <pre><code>import { Component, OnInit } from '@angular/core';
  import { FormBuilder, FormGroup, FormControl } from '@angular/forms';
  import { BlogService } from '../blog-services/blog.service';
  
  @Component({
    selector: 'app-new-blog',
    templateUrl: './new-blog.component.html',
    styleUrls: ['./new-blog.component.css']
  })
  export class NewBlogComponent implements OnInit {
    private newBlogForm: FormGroup;
  
    constructor(private formBuilder: FormBuilder, private blogService: BlogService) { }
  
    ngOnInit() {
      this.newBlogForm = this.formBuilder.group({
        Name: new FormControl(null),
        TileImage: new FormControl(null)
      });
    }
  
    onSubmit(blogData: FormData) {
      console.log('new blog has been submitted.', blogData);
      this.blogService.postBlog(blogData);
      this.newBlogForm.reset();
    }
  
  }
  </code></pre>
  
  <p><code>postBlog</code> from blog.service.ts:</p>
  
  <pre><code>  postBlog(blogData: FormData): Observable&lt;any&gt; {
      const postBlogSubject = new Subject();
      this.appOptions.subscribe(
        (options) =&gt; {
          const url = options.blogAPIUrl + '/Blogs';
          this.http
            .post(url, blogData)
            .subscribe(
              (blog) =&gt; {
                postBlogSubject.next(blog);
              }
            );
        }
      );
      return postBlogSubject.asObservable();
    }
  </code></pre>
  
  <p>The signature for my BlogController looks like this: </p>
  
  <pre class="lang-cs prettyprint-override"><code>[HttpPost]
  public async Task&lt;ActionResult&lt;Blog&gt;&gt; PostBlog([FromForm]PostBlogModel blogModel)
  </code></pre>
  
  <p>with the PostBlogModel as follows:</p>
  
  <pre class="lang-cs prettyprint-override"><code>    public class PostBlogModel
      {
          public string Name { get; set; }
          public IFormFile TileImage { get; set; }
      }
  </code></pre>
  
  <p>I have implemented logging middleware to try to debug. The output is as follows (I see that for some reason the front-end is sending application/json rather than multipart/form-data but I'm not sure why or how to fix...)</p>
  
  <pre><code>blogapi_1  | info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
  blogapi_1  |       Request finished in 170.16740000000001ms 500
  blogapi_1  | info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
  blogapi_1  |       Request starting HTTP/1.1 OPTIONS http://localhost:5432/api/v1/Blogs
  blogapi_1  | dbug: BlogAPI.Middleware.RequestResponseLoggingMiddleware[0]
  blogapi_1  |       HTTP Request: Headers:
  blogapi_1  |            key: Connection, values: keep-alive
  blogapi_1  |            key: Accept, values: */*
  blogapi_1  |            key: Accept-Encoding, values: gzip, deflate, br
  blogapi_1  |            key: Accept-Language, values: en-US,en-IN;q=0.9,en;q=0.8,en-GB;q=0.7
  blogapi_1  |            key: Host, values: localhost:5432
  blogapi_1  |            key: Referer, values: http://localhost:5431/blog/new-blog
  blogapi_1  |            key: User-Agent, values: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
  blogapi_1  |            key: Origin, values: http://localhost:5431
  blogapi_1  |            key: Access-Control-Request-Method, values: POST
  blogapi_1  |            key: Access-Control-Request-Headers, values: content-type
  blogapi_1  |            key: Sec-Fetch-Site, values: same-site
  blogapi_1  |            key: Sec-Fetch-Mode, values: cors
  blogapi_1  |
  blogapi_1  |        type:
  blogapi_1  |        scheme: http
  blogapi_1  |        host+path: localhost:5432/api/v1/Blogs
  blogapi_1  |        queryString:
  blogapi_1  |        body: 
  blogapi_1  | info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
  blogapi_1  |       CORS policy execution successful.
  blogapi_1  | dbug: BlogAPI.Middleware.RequestResponseLoggingMiddleware[0]
  blogapi_1  |       HTTP Response: Headers:
  blogapi_1  |            key: Access-Control-Allow-Headers, values: Content-Type
  blogapi_1  |            key: Access-Control-Allow-Origin, values: http://localhost:5431
  blogapi_1  |
  blogapi_1  |        statusCode: 204
  blogapi_1  |        responseBody: 
  blogapi_1  | info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
  blogapi_1  |       Request finished in 58.5088ms 204
  blogapi_1  | info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
  blogapi_1  |       Request starting HTTP/1.1 POST http://localhost:5432/api/v1/Blogs application/json 56
  blogapi_1  | dbug: BlogAPI.Middleware.RequestResponseLoggingMiddleware[0]
  blogapi_1  |       HTTP Request: Headers:
  blogapi_1  |            key: Connection, values: keep-alive
  blogapi_1  |            key: Content-Type, values: application/json
  blogapi_1  |            key: Accept, values: application/json, text/plain, */*
  blogapi_1  |            key: Accept-Encoding, values: gzip, deflate, br
  blogapi_1  |            key: Accept-Language, values: en-US,en-IN;q=0.9,en;q=0.8,en-GB;q=0.7
  blogapi_1  |            key: Host, values: localhost:5432
  blogapi_1  |            key: Referer, values: http://localhost:5431/blog/new-blog
  blogapi_1  |            key: User-Agent, values: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
  blogapi_1  |            key: Origin, values: http://localhost:5431
  blogapi_1  |            key: Content-Length, values: 56
  blogapi_1  |            key: Sec-Fetch-Site, values: same-site
  blogapi_1  |            key: Sec-Fetch-Mode, values: cors
  blogapi_1  |       
  blogapi_1  |        type: application/json
  blogapi_1  |        scheme: http
  blogapi_1  |        host+path: localhost:5432/api/v1/Blogs
  blogapi_1  |        queryString:
  blogapi_1  |        body: {"Name":"test","TileImage":"C:\\fakepath\\DSC_0327.jpg"}
  </code></pre>