Carga de archivos con Angular2 a la API REST

En realidad, estoy trabajando en una API REST de Spring con una interfaz codificada en Angular 2.

Mi problema es que no puedo cargar un archivo con Angular 2.

Mi Webresources en java es eso:

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

Y funciona perfectamente cuando lo llamo a través de una solicitud URL con el encabezado de autenticación, etc. … (con la extensión Advanced Rest Client para Chrome)

Prueba: (todo funciona bien en ese caso)

enter image description here Añadí el

  

Archivo de configuración de spring y la dependencia de Pom

  commons-fileupload commons-fileupload 1.2  

PERO cuando trato de hacer lo mismo con un formulario web:

  
{{fileContents$|async}}

Con el método (cambio):

 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')); */ } 

Mi servicio web me devuelve un error 500, con eso en los registros de tomcat: http://pastebin.com/PGdcFUQb

Probé el 'Content-Type': undefined método 'Content-Type': undefined también pero sin éxito (el servicio web me devolvió un error 415 en ese caso).

¿Alguien me puede ayudar a descubrir cuál es el problema?

Problema resuelto, voy a actualizar esa pregunta más tarde con mi código 🙂 pero, eche un vistazo en el plunker está funcionando perfectamente bien. Gracias.

Esto es realmente muy fácil de hacer en la versión final. Me tomó un tiempo entenderlo porque la mayoría de la información que he encontrado está desactualizada. Publicando mi solución aquí en caso de que alguien más esté luchando con esto.

 import { Component, ElementRef, Input, ViewChild } from '@angular/core'; import { Http } from '@angular/http'; @Component({ selector: 'file-upload', template: '' }) 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 } } } 

Entonces solo úsalo así:

  

Eso es todo lo que hay.

Alternativamente, capture el objeto del evento y obtenga los archivos del srcElement. No estoy seguro de si alguna forma es mejor que la otra, ¡para ser honesto!

Tenga en cuenta que FormData es IE10 +, por lo que si tiene que admitir IE9 necesitará un relleno policristalino.

Actualización 2017-01-07

Código actualizado para poder manejar la carga de múltiples archivos. Además, a mi respuesta original le faltaba un aspecto bastante crucial relacionado con FormData (dado que moví la lógica de carga real a un servicio independiente en mi propia aplicación, la manejaba allí).

De hecho, en este momento, solo puede proporcionar una entrada de cadena para los métodos de publicación, colocación y patch de la compatibilidad con Angular2 HTTP.

Para respaldar eso, necesita aprovechar el objeto XHR directamente, como se describe a continuación:

 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); }); } } 

Consulte este plunkr para obtener más detalles: https://plnkr.co/edit/ozZqbxIorjQW15BrDFrg?p=info .

Hay un problema y un PR pendiente con respecto a esto en el repo Angular:

Esto me ha funcionado:

  onChange(event: any) { let fileList: FileList = event.target.files; if(fileList.length > 0) { let file: File = fileList[0]; let formData:FormData = new FormData(); formData.append('degree_attachment', file, file.name); let headers = new Headers(); headers.append('Accept', 'application/json'); let options = new RequestOptions({ headers: headers }); this.http.post('http://url', formData,options) .map(res => res.json()) .catch(error => Observable.throw(error)) .subscribe( data => console.log('success'), error => console.log(error) ) }} 

Esto funcionó para mí: Angular 2 proporciona una buena compatibilidad para cargar archivos:

  fileChange(event) { let fileList: FileList = event.target.files; if(fileList.length > 0) { let file: File = fileList[0]; let formData:FormData = new FormData(); formData.append('uploadFile', file, file.name); let headers = new Headers(); headers.append('Content-Type', 'multipart/form-data'); headers.append('Accept', 'application/json'); let options = new RequestOptions({ headers: headers }); this.http.post(URL, formData, options) .map(res => res.json()) .catch(error => Observable.throw(error)) .subscribe( data => console.log('success'), error => console.log(error) ) } } 

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

Para resolver esto, debe eliminar el “tipo de contenido” “multipart / form-data”

Este hilo ha sido tan útil que me sentí obligado a compartir mi solución. La respuesta del hermano Woodrow fue mi punto de partida. También quería llamar la atención sobre el comentario de Rob Gwynn-Jones “asegúrese de no configurar manualmente el encabezado Content-Type”, que es muy importante y me ahorró un montón de tiempo.


Esta versión permite múltiples operaciones de agregar / eliminar (de diferentes carpetas), antes de cargar todos los archivos a la vez.

Se pueden cargar juntos varios archivos con el mismo nombre (de diferentes carpetas), pero el mismo archivo no se agregará a la lista de carga dos veces (¡esto no es tan trivial como parece!).

 import { Component, ElementRef, Input, ViewChild } from '@angular/core'; import { Http } from '@angular/http'; @Component({ selector: 'file-upload', template: '' }) export class FileUploadComponent { @Input() multiple: boolean = false; @ViewChild('fileInput') inputEl: ElementRef; files: Array = []; fileObjects: Array = []; fileKeys: Array = []; fileCount: number = 0; constructor(private http: Http) {} addFiles(callback: any) { const inputEl: HTMLInputElement = this.inputEl.nativeElement; const newCount: number = inputEl.files.length; for (let i = 0; i < newCount; i ++) { const obj = { name: inputEl.files[ i ].name, type: inputEl.files[ i ].type, size: inputEl.files[ i ].size, ts: inputEl.files[ i ].lastModifiedDate }; const key = JSON.stringify(obj); if ( ! this.fileKeys.includes(key)) { this.files.push(inputEl.files.item(i)); this.fileObjects.push(obj); this.fileKeys.push(key); this.fileCount ++; } } callback(this.files); } removeFile(obj: any) { const key: string = JSON.stringify(obj); for (let i = 0; i < this.fileCount; i ++) { if (this.fileKeys[ i ] === key) { this.files.splice(i, 1); this.fileObjects.splice(i, 1); this.fileKeys.splice(i, 1); this.fileCount --; return; } } } } 

Si busca una solución simple y no quiere hacer la encoding usted mismo, recomendaría usar esta biblioteca:

https://www.npmjs.com/package/angular2-http-file-upload

 this.uploader.onBeforeUploadItem = function(item) { item.url = URL.replace('?', "?param1=value1"); }