with servicio funciona ejemplo como spring angular spring-mvc angular2-forms angular2-http

spring - servicio - Carga de archivos con Angular2 a API REST



spring rest (8)

De hecho, por el momento, solo puede proporcionar una entrada de cadena para los métodos de publicación, colocación y patch del soporte Angular2 HTTP.

Para admitir eso, debe 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 RP pendiente con respecto a esto en el repositorio angular:

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

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

Mis recursos web en Java es que:

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

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

Prueba: (todo funciona bien en ese caso)

Agregué el

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

Spring config file y la dependencia de Pom

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

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

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

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

Intenté 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 puede ayudarme a descubrir cuál es el problema?

Problema resuelto, actualizaré esa pregunta más tarde con mi código :) pero, eche un vistazo al plunker, está funcionando perfectamente bien. Gracias.


Este hilo ha sido tan útil que me sentí obligado a compartir mi solución. La respuesta del 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 / quitar (desde diferentes carpetas), antes de cargar todos los archivos a la vez.

Se pueden cargar 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: ''<input type="file" [multiple]="multiple" #fileInput>'' }) export class FileUploadComponent { @Input() multiple: boolean = false; @ViewChild(''fileInput'') inputEl: ElementRef; files: Array<any> = []; fileObjects: Array<any> = []; fileKeys: Array<string> = []; 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; } } } }

La devolución de llamada en ''addFiles'' permite que la carga se realice fuera del componente. El componente se usa así:

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

''setFiles'' es la devolución de llamada. ''this'' en este contexto es el componente padre:

setFiles(files: Array<any>) { this.files = files; }

Todo lo que queda es adjuntar la carga útil multiparte antes de llamar a la API de carga (también en el componente principal):

const formData = new FormData(); for (let i = 0; i < this.files.length; i ++) { formData.append(''file[]'', this.files[ i ]); }

Espero que esto sea útil, y feliz de arreglar / actualizar si es necesario. ¡Aclamaciones!


Esto es realmente 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: ''<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 } } }

Entonces solo úsalo así:

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

Eso es todo lo que hay.

Alternativamente, capture el objeto de evento y obtenga los archivos de srcElement. No estoy seguro 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 polyfill.

Actualizar 2017-01-07

Código actualizado para poder manejar la carga de múltiples archivos. Además, en mi respuesta original faltaba un poco crucial sobre FormData (ya que moví la lógica de carga real a un servicio separado en mi propia aplicación, la estaba manejando allí).


Esto me ha funcionado:

<input type="file" (change)="onChange($event)" required class="form-control " name="attach_file" id="attach_file"> 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 me ha funcionado: Angular 2 proporciona un buen soporte para cargar archivos:

<input type="file" (change)="fileChange($event)" placeholder="Upload file" accept=".pdf,.doc,.docx"> 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 "Contenido-Tipo" "multipart / form-data"



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


fileUpload() { const formData = new FormData(); const files = this.filesToUpload; for (let i = 0; i < files.length; i++) { formData.append(''file'', files.item(i)); formData.append(''Content-Type'', ''application/json''); formData.append(''Accept'', `application/json`); } this.http.post(''http://localhost:8080/UploadFile'', formData).subscribe(response => console.log(response)); }

Entonces:

<form (ngSubmit)="upload()"> <input type="file" id="file" multiple (change)="fileUpload($event.target.files)"> <button type="submit">Upload</button> </form>