55

I know there are a lot of questions about this, but I can't get this to work:

I want to upload a file from input to a server in multipart/form-data

I've tried two approaches. First:

headers: {
  'Content-Type': undefined
},

Which results in e.g. for an image

Content-Type:image/png

while it should be multipart/form-data

and the other:

headers: {
  'Content-Type': multipart/form-data
},

But this asks for a boundry header, which I believe should not be manually inserted...

What is a clean way to solve this problem? I've read that you can do

$httpProvider.defaults.headers.post['Content-Type'] = 'multipart/form-data; charset=utf-8';

But I don't want all my posts to be multipart/form-data. The default should be JSON

Thomas Stubbe
  • 1,945
  • 5
  • 26
  • 40

6 Answers6

86

Take a look at the FormData object: https://developer.mozilla.org/en/docs/Web/API/FormData

this.uploadFileToUrl = function(file, uploadUrl){
        var fd = new FormData();
        fd.append('file', file);
        $http.post(uploadUrl, fd, {
            transformRequest: angular.identity,
            headers: {'Content-Type': undefined}
        })
        .success(function(){
        })
        .error(function(){
        });
    }
jstuartmilne
  • 4,398
  • 1
  • 20
  • 30
  • Just confirmed and IE 9 must no longer be supported for this project, so this is perfect for me! Thx! – Thomas Stubbe Mar 01 '16 at 12:50
  • 15
    This works but why does the `Content-Type`has to be set to `undefined` ? – meucaa Aug 21 '17 at 13:21
  • 7
    @meucaa The code snippet looks similar to what's on this blog post which states: "By setting ‘Content-Type’: undefined, the browser sets the Content-Type to multipart/form-data for us and fills in the correct boundary. Manually setting ‘Content-Type’: multipart/form-data will fail to fill in the boundary parameter of the request." - https://www.uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs/ – gavxn Oct 23 '18 at 15:50
  • 1
    instead of file if i have base64(blob) how can i upload ? @jstuartmilne – bhaRATh Mar 27 '19 at 07:19
  • 1
    I am working with an ASP.NET Web API backend, and this is exactly what was needed! I didn't need the `transformRequest` bit, however. Thank you! – Steve Danner Nov 11 '19 at 19:19
38

Here's an updated answer for Angular 4 & 5. TransformRequest and angular.identity were dropped. I've also included the ability to combine files with JSON data in one request.

Angular 5 Solution:

import {HttpClient} from '@angular/common/http';

uploadFileToUrl(files, restObj, uploadUrl): Promise<any> {
  // Note that setting a content-type header
  // for mutlipart forms breaks some built in
  // request parsers like multer in express.
  const options = {} as any; // Set any options you like
  const formData = new FormData();

  // Append files to the virtual form.
  for (const file of files) {
    formData.append(file.name, file)
  }

  // Optional, append other kev:val rest data to the form.
  Object.keys(restObj).forEach(key => {
    formData.append(key, restObj[key]);
  });

  // Send it.
  return this.httpClient.post(uploadUrl, formData, options)
    .toPromise()
    .catch((e) => {
      // handle me
    });
}

Angular 4 Solution:

// Note that these imports below are deprecated in Angular 5
import {Http, RequestOptions} from '@angular/http';

uploadFileToUrl(files, restObj, uploadUrl): Promise<any> {
  // Note that setting a content-type header
  // for mutlipart forms breaks some built in
  // request parsers like multer in express.
  const options = new RequestOptions();
  const formData = new FormData();

  // Append files to the virtual form.
  for (const file of files) {
    formData.append(file.name, file)
  }

  // Optional, append other kev:val rest data to the form.
  Object.keys(restObj).forEach(key => {
    formData.append(key, restObj[key]);
  });

  // Send it.
  return this.http.post(uploadUrl, formData, options)
    .toPromise()
    .catch((e) => {
      // handle me
    });
}
Josh Hibschman
  • 3,148
  • 1
  • 25
  • 27
5

In Angular 6, you can do this:

In your service file:

 function_name(data) {
    const url = `the_URL`;
    let input = new FormData();
    input.append('url', data);   // "url" as the key and "data" as value
    return this.http.post(url, input).pipe(map((resp: any) => resp));
  }

In component.ts file: in any function say xyz,

xyz(){
this.Your_service_alias.function_name(data).subscribe(d => {   // "data" can be your file or image in base64 or other encoding
      console.log(d);
    });
}
Abhishek Gupta
  • 118
  • 2
  • 5
3

In Angular 13 (~13.0.0) isn't need to specify Content-Type of request.

some.service.ts:

public sendFiles(data: any): Observable<any> {
    const formData = new FormData();

    data.files.forEach((file: File) => {
      formData.append('files', file);
    });
    
    const url: string = <YOUR_API_URL>;

    return this.http.post<any>(url, formData);
}
  • Set Content-Type field to the following values ​​will result in error:
    • 'file'
    • undefined (can't with strict mode active)
    • 'multipart/form-data'
2

In angular 9, Before tried 'Content-Type': undefined, but it is not worked for me then I tried the below code and It works like charms for a file object

const request = this.http.post(url, data, {
      headers: {
        'Content-Type': 'file'
      },
    });
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Kamalakar
  • 389
  • 2
  • 9
1

Do not Set, Reset or Update the built in FormData's Content-Type. It will be solved. In Angular 7 it works.

Though there was an Auth-Interceptor But I did not let it set, change or reset my Content-Type header of my post request. Let it be what it is and the problem is solved.

Here is my auth interceptor image where I by passed my request

Kazi Rumi
  • 11
  • 2