使用Angular2将file upload到Rest API
实际上,我正在使用angular度为2的接口编写Spring API REST。
我的问题是我无法上传angular度为2的文件。
我在Java中的Webresources是:
@RequestMapping(method = RequestMethod.POST, value = "/upload") public String handleFileUpload(@RequestParam MultipartFile file) { //Dosomething }
当我通过URL请求与Auth头等呼叫它时,它是完全正常工作…(与先进的rest客户端扩展为Chrome)
certificate:(在这种情况下一切正常)
我加了
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
Springconfiguration文件和POM依赖
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.2</version> </dependency>
但是当我尝试用webform做同样的事情时:
<input type="file" #files (change)="change(files)"/> <pre>{{fileContents$|async}}</pre>
使用(更改)方法:
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')); */ }
我的web服务返回错误500,在tomcat日志中: http : //pastebin.com/PGdcFUQb
我尝试了'Content-Type':未定义的方法,但没有成功(在这种情况下,Web服务返回了一个415错误。
有人能帮我弄清楚是什么问题?
问题解决了,稍后我会用我的代码更新这个问题:)但是,看看它的工作非常好。 谢谢。
这在最终版本中确实很容易实现。 花了我一段时间来围绕它,因为我所遇到的大部分信息已经过时了。 在这里发布我的解决scheme,以防其他人正在为此而苦苦挣扎。
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 } } }
然后就像这样使用它:
<file-upload #fu (change)="fu.upload()" [multiple]="true"></file-upload>
这确实是所有这一切。
或者,捕获事件对象并从srcElement获取文件。 不知道有没有比其他方式更好,说实话!
请记住FormData是IE10 +,所以如果你必须支持IE9,你需要一个polyfill。
更新2017-01-07
更新后的代码能够处理上传多个文件。 另外我的原始答案是缺less一个关于FormData相当重要的位(因为我把实际的上传逻辑移到我自己的应用程序中的一个单独的服务,我正在处理它)。
实际上,目前,您只能为Angular2 HTTP支持的post
, put
和patch
方法提供stringinput。
为了支持这一点,您需要直接利用XHR对象,如下所述:
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); }); } }
看到这个PLUNK的更多细节: https ://plnkr.co/edit/ozZqbxIorjQW15BrDFrg?p = info。
在Angular回购中有一个问题和一个待决的PR:
这对我有效:
<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) ) }}
这对我有效:Angular 2提供了很好的支持上传文件:
<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
为了解决这个问题,你应该删除“Content-Type”“multipart / form-data”
this.uploader.onBeforeUploadItem = function(item) { item.url = URL.replace('?', "?param1=value1"); }
这个线程非常有帮助,我感到不得不分享我的解决scheme。 伍德罗弟兄的回答是我的出发点。 我还想提请注意Rob Gwynn-Jones的评论“确保不要手动设置Content-Type标题”这是非常重要的,为我节省了大量的时间。
此版本允许多次添加/删除操作(从不同的文件夹),在一次上传所有文件之前。
具有相同名称(来自不同文件夹)的多个文件可以一起上传,但是相同的文件不会被添加到上传列表两次(这不像看起来那么微不足道)。
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; } } } }
如果你正在寻找一个简单的解决scheme,而不想自己编写代码,我会推荐使用这个库:
- 如何在Spring中自动assemblygenerics<T>的bean?
- Spring中的BeanPostProcessor和init / destroy方法有什么区别?
- 在应用程序启动时使用Spring MVC执行Java类
- 使用@PropertySource批注时,@Value未parsing。 如何configurationPropertySourcesPlaceholderConfigurer?
- 春季启动:是否有可能使用外部application.properties文件在任何目录与胖胖的jar?
- 将applicationContext分割为多个文件
- 如何在春季做条件自动布线?
- 尝试使用Spring Data JPA创build自定义存储库时找不到types错误的属性
- 我应该为业务层使用EJB3还是Spring?