207

I know this is very a general question but I am failing to upload a file in Angular 2. I have tried

1) http://valor-software.com/ng2-file-upload/ and

2) http://ng2-uploader.com/home

...but failed. Has anyone uploaded a file in Angular? What method did you use? How to do so? If any sample code or demo link is provided it will be really appreciated.

Lazar Ljubenović
  • 18,976
  • 10
  • 56
  • 91
heman123
  • 3,009
  • 4
  • 24
  • 37

14 Answers14

422

Angular 2 provides good support for uploading files. No third party library is required.

<input type="file" (change)="fileChange($event)" placeholder="Upload file" accept=".pdf,.doc,.docx">
fileChange(event) {
    let fileList: FileList = event.target.files;

    if (fileList.length < 1) {
      return;
    }
    
    let file: File = fileList[0];
    let formData:FormData = new FormData();
    formData.append('uploadFile', file, file.name)
    
    let headers = new Headers();
    /** In Angular 5, including the header Content-Type can invalidate your request */
    headers.append('Content-Type', 'multipart/form-data');
    headers.append('Accept', 'application/json');

    let options = new RequestOptions({ headers: headers });

    this.http.post(`${this.apiEndPoint}`, formData, options)
        .map(res => res.json())
        .catch(error => Observable.throw(error))
        .subscribe(
            data => console.log('success'),
            error => console.log(error)
        );
}

using @angular/core": "~2.0.0" and @angular/http: "~2.0.0"

doncadavona
  • 7,162
  • 9
  • 41
  • 54
Eswar
  • 4,923
  • 1
  • 12
  • 17
  • 1
    ${this.apiEndPoint} is showing error " unresolved variable" ! how to solve this ? Where can i read about file upload in angular2? which site ? which doc provide about the detailed information? – heman123 Oct 24 '16 at 11:17
  • 7
    it is not working, at least in my case. sailsJs server receive empty file array/object – Kaleem Ullah Mar 07 '17 at 07:12
  • 22
    It worked for me except - I had to work on this line- `headers.append('enctype', 'multipart/form-data');` (used 'enctype' to replace 'Content-Type'). Perhaps it depends on the server-side code. (i.e. api) – Ariful Islam Apr 07 '17 at 09:59
  • @Eswar how would I add a progress to this solution? It seems nicer than native XHR approach. – Konrad Apr 26 '17 at 14:38
  • 42
    Be great if the Angular team would write some documentation on the subject, I can't find a single line about it in their docs. This code sample is out of date and doesn't work with v4+. – Rob B May 09 '17 at 02:15
  • 16
    Note for some application servers, setting the content-type will be rejected. You need to let it be blank: let headers = new Headers(); The browser will sort it all out for you. – PeterS May 22 '17 at 16:58
  • Yes. Just let headers = new Headers(); worked in my case with Angular 4 – makkasi May 31 '17 at 07:23
  • Any idea why angular2 need api to save file to the server? – Krishna Mohan Jun 06 '17 at 04:32
  • Trying to do same but getting my coder is getting error - can anyone help us here - https://stackoverflow.com/questions/44471832/file-upload-not-working-with-angular-2-php – user2828442 Jun 10 '17 at 09:59
  • 8
    LMFAO struggled 20mins with this crap until I realized that I didnt needed to set the headers at all. Note to others using angular 4.x.x with .Net webapi, dont try to set the headers! Thx for pointing that out @PeterS – Jota.Toledo Jun 29 '17 at 12:18
  • How can i upload a large file like 500MB using chunk? any idea – Jitendra Solanki Sep 01 '17 at 10:25
  • 2
    I need to upload it as application/json , is it possible? – artdias90 Sep 06 '17 at 15:07
  • Wih `Rails` as back-end and Angular v4 (using `HttpClient`) I didn't need to set nothing, just call like this: `this.httpClient.post(URL, formData)...;` – developer033 Sep 08 '17 at 17:11
  • 1
    there's a helpful post at Medium.com: https://medium.com/codingthesmartway-com-blog/angular-4-3-httpclient-accessing-rest-web-services-with-angular-2305b8fd654b which describes the new HttpClient API a bit – jsaddwater Oct 25 '17 at 09:41
  • It works perfect but the only issue is that i cannot disable **multipart data** with this. – fiza khan Dec 18 '17 at 07:18
  • I am not able to get data in formData. its giving me blank object {}. O.S(Linux). – Prasad Shinde Feb 26 '18 at 07:01
  • Ohmighosh--two developers wasted a day and a half because we were using Angular 4 and trying to supply headers. – Flea Apr 10 '18 at 20:38
  • You saved me on the "/** In Angular 5, including the header Content-Type can invalidate your request */" - Was having a big problem because of this. – Raven Apr 24 '18 at 17:45
  • For IE 10+ it is important to add the filename seperately as @Eswar did – der_chirurg Jun 20 '18 at 13:55
  • this post does a lot you don't need, and also in almost any case this would be split into a service file, and a component that subscribes to it – Sam Alexander Jan 08 '19 at 16:25
88

From the answers above I build this with Angular 5.x

Just call uploadFile(url, file).subscribe() to trigger an upload

import { Injectable } from '@angular/core';
import {HttpClient, HttpParams, HttpRequest, HttpEvent} from '@angular/common/http';
import {Observable} from "rxjs";

@Injectable()
export class UploadService {

  constructor(private http: HttpClient) { }

  // file from event.target.files[0]
  uploadFile(url: string, file: File): Observable<HttpEvent<any>> {

    let formData = new FormData();
    formData.append('upload', file);

    let params = new HttpParams();

    const options = {
      params: params,
      reportProgress: true,
    };

    const req = new HttpRequest('POST', url, formData, options);
    return this.http.request(req);
  }
}

Use it like this in your component

  // At the drag drop area
  // (drop)="onDropFile($event)"
  onDropFile(event: DragEvent) {
    event.preventDefault();
    this.uploadFile(event.dataTransfer.files);
  }

  // At the drag drop area
  // (dragover)="onDragOverFile($event)"
  onDragOverFile(event) {
    event.stopPropagation();
    event.preventDefault();
  }

  // At the file input element
  // (change)="selectFile($event)"
  selectFile(event) {
    this.uploadFile(event.target.files);
  }

  uploadFile(files: FileList) {
    if (files.length == 0) {
      console.log("No file selected!");
      return

    }
    let file: File = files[0];

    this.upload.uploadFile(this.appCfg.baseUrl + "/api/flash/upload", file)
      .subscribe(
        event => {
          if (event.type == HttpEventType.UploadProgress) {
            const percentDone = Math.round(100 * event.loaded / event.total);
            console.log(`File is ${percentDone}% loaded.`);
          } else if (event instanceof HttpResponse) {
            console.log('File is completely loaded!');
          }
        },
        (err) => {
          console.log("Upload Error:", err);
        }, () => {
          console.log("Upload done");
        }
      )
  }
Tarion
  • 16,283
  • 13
  • 71
  • 107
  • 6
    Works fine with Angular6. Thank you. And you need these libraries to import. import {HttpClient, HttpParams, HttpRequest, HttpEvent, HttpEventType, HttpResponse} from '@angular/common/http'; – Bharathiraja Jun 28 '18 at 12:39
  • 1
    in my case I was using authorization bearer and added this extra code `let params = new HttpParams(); let headers = new HttpHeaders({ 'Authorization': 'Bearer ' + localStorage.getItem('accessToken'), }); const options = { headers: headers, params: params, reportProgress: true, };` – Ciprian Dragoe Jul 10 '18 at 09:27
  • It's worth noting that the imports for `Observable` and `HttpEvent` could be omitted entirely if you're okay with using type inference to provide the function's return type for `uploadFile()`! `this.http.request()` already returns a type of `Observable>`, so if you give the request call a generic type (i.e. `this.http.request()` then the whole function just works out with the right types. – wosevision Oct 11 '18 at 20:05
  • 2
    The html part goes like this `input type="file" (change)="addFiles($event)" style="display: none" #file multiple> ` – Shantam Mittal Apr 29 '19 at 05:57
  • What is 'upload' in -> formData.append('upload', file); in UploadService ? – ShaileshDev Jun 08 '22 at 14:12
26

Thanks to @Eswar. This code worked perfectly for me. I want to add certain things to the solution :

I was getting error : java.io.IOException: RESTEASY007550: Unable to get boundary for multipart

In order to solve this error, you should remove the "Content-Type" "multipart/form-data". It solved my problem.

Amir
  • 8,821
  • 7
  • 44
  • 48
heman123
  • 3,009
  • 4
  • 24
  • 37
  • 8
    +1. If you remove Content-Type, it gets generated correctly. E.g.: `multipart/form-data; boundary=---------------------------186035562730765173675680113`. Also see http://stackoverflow.com/a/29697774/1475331 and https://github.com/angular/angular/issues/11819. – turdus-merula Mar 23 '17 at 11:18
  • 1
    I'm getting this error `java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found"` which is similar to yours, however when I remove the `Content-Type` header I get a 404 from the backend instead. We're using Spring and Angular 2. Any help appreciated. – Helen Sep 26 '17 at 05:23
  • This should be just a comment on his answer, shouldn't it? – MMalke Jun 29 '18 at 13:28
  • Thanks, but why doesn't it work with the "Content-type" header? – Aboubacar Ouattara Aug 08 '20 at 12:16
20

Since the code sample is a bit outdated I thought I'd share a more recent approach, using Angular 4.3 and the new(er) HttpClient API, @angular/common/http

export class FileUpload {

@ViewChild('selectedFile') selectedFileEl;

uploadFile() {
let params = new HttpParams();

let formData = new FormData();
formData.append('upload', this.selectedFileEl.nativeElement.files[0])

const options = {
    headers: new HttpHeaders().set('Authorization', this.loopBackAuth.accessTokenId),
    params: params,
    reportProgress: true,
    withCredentials: true,
}

this.http.post('http://localhost:3000/api/FileUploads/fileupload', formData, options)
.subscribe(
    data => {
        console.log("Subscribe data", data);
    },
    (err: HttpErrorResponse) => {
        console.log(err.message, JSON.parse(err.error).error.message);
    }
)
.add(() => this.uploadBtn.nativeElement.disabled = false);//teardown
}
jsaddwater
  • 1,781
  • 2
  • 18
  • 28
  • 1
    do you have the html for this? I like that this is using HttpParams. Just wondering if you have a complete working example somewhere. Thanks – Maddy Jun 14 '18 at 15:54
  • In this way how can I upload multiple files together as an array? how it should append to the form data object? – SSR Aug 10 '18 at 06:04
  • have a look at multipart form data https://www.webdavsystem.com/javaserver/doc/resumable_upload/multipart_post/ – jsaddwater Aug 13 '18 at 12:14
19

In Angular 2+, it is very important to leave the Content-Type empty. If you set the 'Content-Type' to 'multipart/form-data' the upload will not work !

upload.component.html

<input type="file" (change)="fileChange($event)" name="file" />

upload.component.ts

export class UploadComponent implements OnInit {
    constructor(public http: Http) {}

    fileChange(event): void {
        const fileList: FileList = event.target.files;
        if (fileList.length > 0) {
            const file = fileList[0];

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

            const headers = new Headers();
            // It is very important to leave the Content-Type empty
            // do not use headers.append('Content-Type', 'multipart/form-data');
            headers.append('Authorization', 'Bearer ' + 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9....');
            const options = new RequestOptions({headers: headers});

            this.http.post('https://api.mysite.com/uploadfile', formData, options)
                 .map(res => res.json())
                 .catch(error => Observable.throw(error))
                 .subscribe(
                     data => console.log('success'),
                     error => console.log(error)
                 );
        }
    }
}
abahet
  • 10,355
  • 4
  • 32
  • 23
8

I have used the following tool from priming with success. I have no skin in the game with primeNg, just passing on my suggestion.

http://www.primefaces.org/primeng/#/fileupload

John Baird
  • 2,606
  • 1
  • 14
  • 33
8

This simple solution worked for me: file-upload.component.html

<div>
  <input type="file" #fileInput placeholder="Upload file..." />
  <button type="button" (click)="upload()">Upload</button>
</div>

And then do the upload in the component directly with XMLHttpRequest.

import { Component, OnInit, ViewChild } from '@angular/core';

@Component({
  selector: 'app-file-upload',
  templateUrl: './file-upload.component.html',
  styleUrls: ['./file-upload.component.css']
})
export class FileUploadComponent implements OnInit {

  @ViewChild('fileInput') fileInput;

  constructor() { }

  ngOnInit() {
  }

  private upload() {
    const fileBrowser = this.fileInput.nativeElement;
    if (fileBrowser.files && fileBrowser.files[0]) {
      const formData = new FormData();
      formData.append('files', fileBrowser.files[0]);
      const xhr = new XMLHttpRequest();
      xhr.open('POST', '/api/Data/UploadFiles', true);
      xhr.onload = function () {
        if (this['status'] === 200) {
            const responseText = this['responseText'];
            const files = JSON.parse(responseText);
            //todo: emit event
        } else {
          //todo: error handling
        }
      };
      xhr.send(formData);
    }
  }

}

If you are using dotnet core, the parameter name must match the from field name. files in this case:

[HttpPost("[action]")]
public async Task<IList<FileDto>> UploadFiles(List<IFormFile> files)
{
  return await _binaryService.UploadFilesAsync(files);
}

This answer is a plagiate of http://blog.teamtreehouse.com/uploading-files-ajax

Edit: After uploading, you have to clear the file-upload so that the user can select a new file. And instead of using XMLHttpRequest, maybe it is better to use fetch:

private addFileInput() {
    const fileInputParentNative = this.fileInputParent.nativeElement;
    const oldFileInput = fileInputParentNative.querySelector('input');
    const newFileInput = document.createElement('input');
    newFileInput.type = 'file';
    newFileInput.multiple = true;
    newFileInput.name = 'fileInput';
    const uploadfiles = this.uploadFiles.bind(this);
    newFileInput.onchange = uploadfiles;
    oldFileInput.parentNode.replaceChild(newFileInput, oldFileInput);
  }

  private uploadFiles() {
    this.onUploadStarted.emit();
    const fileInputParentNative = this.fileInputParent.nativeElement;
    const fileInput = fileInputParentNative.querySelector('input');
    if (fileInput.files && fileInput.files.length > 0) {
      const formData = new FormData();
      for (let i = 0; i < fileInput.files.length; i++) {
        formData.append('files', fileInput.files[i]);
      }

      const onUploaded = this.onUploaded;
      const onError = this.onError;
      const addFileInput = this.addFileInput.bind(this);
      fetch('/api/Data/UploadFiles', {
        credentials: 'include',
        method: 'POST',
        body: formData,
      }).then((response: any) => {
        if (response.status !== 200) {
          const error = `An error occured. Status: ${response.status}`;
          throw new Error(error);
        }
        return response.json();
      }).then(files => {
        onUploaded.emit(files);
        addFileInput();
      }).catch((error) => {
        onError.emit(error);
      });
    }

https://github.com/yonexbat/cran/blob/master/cranangularclient/src/app/file-upload/file-upload.component.ts

yonexbat
  • 2,902
  • 2
  • 32
  • 43
3

This is useful tutorial, how to upload file using the ng2-file-upload and WITHOUT ng2-file-upload.

For me it helps a lot.

At the moment, tutorial contains a couple of mistakes:

1- Client should have same upload url as a server, so in app.component.ts change line

const URL = 'http://localhost:8000/api/upload';

to

const URL = 'http://localhost:3000';

2- Server send response as 'text/html', so in app.component.ts change

.post(URL, formData).map((res:Response) => res.json()).subscribe(
  //map the success function and alert the response
  (success) => {
    alert(success._body);
  },
  (error) => alert(error))

to

.post(URL, formData)  
.subscribe((success) => alert('success'), (error) => alert(error));
Vincent D'amour
  • 3,746
  • 1
  • 27
  • 39
Sandre
  • 498
  • 6
  • 10
3

To upload image with form fields

SaveFileWithData(article: ArticleModel,picture:File): Observable<ArticleModel> 
{

    let headers = new Headers();
    // headers.append('Content-Type', 'multipart/form-data');
    // headers.append('Accept', 'application/json');

let requestoptions = new RequestOptions({
  method: RequestMethod.Post,
  headers:headers
    });



let formData: FormData = new FormData();
if (picture != null || picture != undefined) {
  formData.append('files', picture, picture.name);
}
 formData.append("article",JSON.stringify(article));

return this.http.post("url",formData,requestoptions)
  .map((response: Response) => response.json() as ArticleModel);
} 

In my case I needed .NET Web Api in C#

// POST: api/Articles
[ResponseType(typeof(Article))]
public async Task<IHttpActionResult> PostArticle()
{
    Article article = null;
    try
    {

        HttpPostedFile postedFile = null;
        var httpRequest = HttpContext.Current.Request;

        if (httpRequest.Files.Count == 1)
        {
            postedFile = httpRequest.Files[0];
            var filePath = HttpContext.Current.Server.MapPath("~/" + postedFile.FileName);
            postedFile.SaveAs(filePath);
        }
        var json = httpRequest.Form["article"];
         article = JsonConvert.DeserializeObject <Article>(json);

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        article.CreatedDate = DateTime.Now;
        article.CreatedBy = "Abbas";

        db.articles.Add(article);
        await db.SaveChangesAsync();
    }
    catch (Exception ex)
    {
        int a = 0;
    }
    return CreatedAtRoute("DefaultApi", new { id = article.Id }, article);
}
Charlie
  • 4,827
  • 2
  • 31
  • 55
3

Today I was integrated ng2-file-upload package to my angular 6 application, It was pretty simple, Please find the below high-level code.

import the ng2-file-upload module

app.module.ts

    import { FileUploadModule } from 'ng2-file-upload';

    ------
    ------
    imports:      [ FileUploadModule ],
    ------
    ------

Component ts file import FileUploader

app.component.ts

    import { FileUploader, FileLikeObject } from 'ng2-file-upload';
    ------
    ------
    const URL = 'http://localhost:3000/fileupload/';
    ------
    ------

     public uploader: FileUploader = new FileUploader({
        url: URL,
        disableMultipart : false,
        autoUpload: true,
        method: 'post',
        itemAlias: 'attachment'

        });

      public onFileSelected(event: EventEmitter<File[]>) {
        const file: File = event[0];
        console.log(file);

      }
    ------
    ------

Component HTML add file tag

app.component.html

 <input type="file" #fileInput ng2FileSelect [uploader]="uploader" (onFileSelected)="onFileSelected($event)" />

Working Online stackblitz Link: https://ng2-file-upload-example.stackblitz.io

Stackblitz Code example: https://stackblitz.com/edit/ng2-file-upload-example

Official documentation link https://valor-software.com/ng2-file-upload/

Raja Rama Mohan Thavalam
  • 8,131
  • 2
  • 31
  • 30
2

Try not setting the options parameter

this.http.post(${this.apiEndPoint}, formData)

and make sure you are not setting the globalHeaders in your Http factory.

thxmxx
  • 447
  • 4
  • 9
1

jspdf and Angular 8

I generate a pdf and want to upload the pdf with POST request, this is how I do (For clarity, I delete some of the code and service layer)

import * as jsPDF from 'jspdf';
import { HttpClient } from '@angular/common/http';

constructor(private http: HttpClient)

upload() {
    const pdf = new jsPDF()
    const blob = pdf.output('blob')
    const formData = new FormData()
    formData.append('file', blob)
    this.http.post('http://your-hostname/api/upload', formData).subscribe()
}
Brady Huang
  • 1,852
  • 20
  • 23
0

I've upload file using reference. No package is required to upload file this way.

// code to be written in .ts file

@ViewChild("fileInput") fileInput;

addFile(): void {
let fi = this.fileInput.nativeElement;
if (fi.files && fi.files[0]) {
  let fileToUpload = fi.files[0];
    this.admin.addQuestionApi(fileToUpload)
      .subscribe(
        success => {
          this.loading = false;
          this.flashMessagesService.show('Uploaded successfully', {
            classes: ['alert', 'alert-success'],
            timeout: 1000,
          });
        },
        error => {
          this.loading = false;
          if(error.statusCode==401) this.router.navigate(['']);
          else
            this.flashMessagesService.show(error.message, {
              classes: ['alert', 'alert-danger'],
              timeout: 1000,
            });
        });
  }

}

// code to be written in service.ts file

addQuestionApi(fileToUpload: any){
var headers = this.getHeadersForMultipart();
let input = new FormData();
input.append("file", fileToUpload);

return this.http.post(this.baseUrl+'addQuestions', input, {headers:headers})
      .map(response => response.json())
      .catch(this.errorHandler);

}

// code to be written in html

<input type="file" #fileInput>
Sheena Singla
  • 706
  • 7
  • 13
-1

In the simplest form, the following code works in Angular 6/7

this.http.post("http://destinationurl.com/endpoint", fileFormData)
  .subscribe(response => {
    //handle response
  }, err => {
    //handle error
  });

Here is the complete implementation

shaheer shukur
  • 1,077
  • 2
  • 12
  • 19