Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

File Upload with Angular2 to REST API

Actually, I'm working on a Spring REST API with an interface coded in Angular 2.

My problem is I can't upload a file with Angular 2.

My Webresources in java is that :

@RequestMapping(method = RequestMethod.POST, value = "/upload") public String handleFileUpload(@RequestParam MultipartFile file) {     //Dosomething  } 

And it is perfectly working when I call it through URL request with Auth header etc ... ( with Advanced Rest Client extension for Chrome )

Proof: (everything works fine in that case )

enter image description here I added the

<bean id="multipartResolver"       class="org.springframework.web.multipart.commons.CommonsMultipartResolver" /> 

Spring config file and the Pom dependency

<dependency>     <groupId>commons-fileupload</groupId>     <artifactId>commons-fileupload</artifactId>     <version>1.2</version> </dependency> 

BUT when I try to do the same thing with a webform :

<input type="file" #files (change)="change(files)"/> <pre>{{fileContents$|async}}</pre> 

With the (change) method :

change(file) {     let formData = new FormData();     formData.append("file", file);     console.log(formData);     let headers = new Headers({         'Authorization': 'Bearer ' + this.token,         'Content-Type': 'multipart/form-data'     });     this.http.post(this.url, formData, {headers}).map(res => res.json()).subscribe((data) => console.log(data));     /*     Observable.fromPromise(fetch(this.url,         {method: 'post', body: formData},         {headers: this.headers}     )).subscribe(()=>console.log('done'));     */ } 

My web service returns me an error 500, with that in tomcat logs: http://pastebin.com/PGdcFUQb

I tried the 'Content-Type': undefined method too but without success ( the web service return me a 415 error in that case.

Can someone help me to figure out what's the problem is?

Problem solved, I'll update that question later with my code :) but, have a look on the plunker it's working perfectly well. Thanks.

like image 990
Slater Avatar asked Apr 01 '16 09:04

Slater


2 Answers

This is actually really easy to do in the final release. Took me a while to wrap my head around it because most information about it that I've come across is outdated. Posting my solution here in case anyone else is struggling with this.

import { Component, ElementRef, Input, ViewChild } from '@angular/core'; import { Http } from '@angular/http';  @Component({     selector: 'file-upload',     template: '<input type="file" [multiple]="multiple" #fileInput>' }) export class FileUploadComponent {     @Input() multiple: boolean = false;     @ViewChild('fileInput') inputEl: ElementRef;      constructor(private http: Http) {}      upload() {         let inputEl: HTMLInputElement = this.inputEl.nativeElement;         let fileCount: number = inputEl.files.length;         let formData = new FormData();         if (fileCount > 0) { // a file was selected             for (let i = 0; i < fileCount; i++) {                 formData.append('file[]', inputEl.files.item(i));             }             this.http                 .post('http://your.upload.url', formData)                 // do whatever you do...                 // subscribe to observable to listen for response         }     } } 

Then just use it like so:

<file-upload #fu (change)="fu.upload()" [multiple]="true"></file-upload> 

That is really all there is to it.

Alternatively, capture the event object and get the files from the srcElement. Not sure if any way is better than the other, to be honest!

Keep in mind FormData is IE10+, so if you have to support IE9 you'll need a polyfill.

Update 2017-01-07

Updated code to be able to handle uploading of multiple files. Also my original answer was missing a rather crucial bit concerning FormData (since I moved the actual upload logic to a separate service in my own app I was handling it there).

like image 66
Brother Woodrow Avatar answered Sep 18 '22 12:09

Brother Woodrow


In fact, at the moment, you can only provide string input for post, put and patch methods of the Angular2 HTTP support.

To support that, you need to leverage the XHR object directly, as described below:

import {Injectable} from 'angular2/core'; import {Observable} from 'rxjs/Rx';  @Injectable() export class UploadService {   constructor () {     this.progress$ = Observable.create(observer => {       this.progressObserver = observer     }).share();   }    private makeFileRequest (url: string, params: string[], files: File[]): Observable {     return Observable.create(observer => {       let formData: FormData = new FormData(),         xhr: XMLHttpRequest = new XMLHttpRequest();        for (let i = 0; i < files.length; i++) {         formData.append("uploads[]", files[i], files[i].name);       }        xhr.onreadystatechange = () => {         if (xhr.readyState === 4) {           if (xhr.status === 200) {             observer.next(JSON.parse(xhr.response));             observer.complete();           } else {             observer.error(xhr.response);           }         }       };        xhr.upload.onprogress = (event) => {         this.progress = Math.round(event.loaded / event.total * 100);          this.progressObserver.next(this.progress);       };        xhr.open('POST', url, true);       xhr.send(formData);     });   } } 

See this plunkr for more details: https://plnkr.co/edit/ozZqbxIorjQW15BrDFrg?p=info.

There is a an issue and a pending PR regarding this in the Angular repo:

  • https://github.com/angular/angular/issues/10424
  • https://github.com/angular/angular/pull/7310/files
like image 20
Thierry Templier Avatar answered Sep 20 '22 12:09

Thierry Templier