Загрузка файла с помощью Angular2 в Rest API
На самом деле, я работаю над API REST с интерфейсом, закодированным на angular 2.
Моя проблема в том, что я не могу загрузить файл с angular 2.
My Webresources в java заключается в следующем:
@RequestMapping(method = RequestMethod.POST, value = "/upload")
public String handleFileUpload(@RequestParam MultipartFile file) {
//Dosomething
}
И он отлично работает, когда я вызываю его через URL-запрос с заголовком Auth и т.д....
(с расширением Advanced Rest Client для Chrome)
Доказательство: (все работает отлично в этом случае)
Я добавил
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
Spring конфигурационный файл и зависимость Pom
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2</version>
</dependency>
НО, когда я пытаюсь сделать то же самое с веб-формой:
<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'));
*/
}
Мой веб-сервис возвращает мне ошибку 500, а в журналах tomcat: http://pastebin.com/PGdcFUQb
Я попробовал метод Content-Type: undefined, но безуспешно (веб-служба возвращает мне ошибку 415 в этом случае.
Может кто-нибудь помочь мне разобраться, в чем проблема?
Проблема решена, я уточню этот вопрос позже с моим кодом:), но посмотрите на плункер, который работает отлично.
Спасибо.
Ответы
Ответ 1
Это действительно очень легко сделать в финальной версии. Потратил меня на то, чтобы обернуть вокруг меня голову, потому что большая часть информации о ней, с которой я столкнулась, устарела. Публикация моего решения здесь, если кто-то еще борется с этим.
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
Обновлен код, позволяющий обрабатывать загрузку нескольких файлов. Также в моем первоначальном ответе отсутствовал довольно важный бит, касающийся FormData (поскольку я переместил фактическую логику загрузки в отдельную службу в своем собственном приложении, я обрабатывал ее там).
Ответ 2
Фактически, на данный момент вы можете предоставить только строковый ввод для post
, put
и patch
методов поддержки Angular2 HTTP.
Чтобы поддержать это, вам необходимо напрямую использовать объект 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);
});
}
}
Смотрите этот plunkr для более подробной информации: https://plnkr.co/edit/ozZqbxIorjQW15BrDFrg?p=info.
В репозитории Angular есть проблема и ожидающий PR-запрос:
Ответ 3
Это сработало для меня:
<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)
)
}}
Ответ 4
Это сработало для меня: 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"
Ответ 5
this.uploader.onBeforeUploadItem = function(item) {
item.url = URL.replace('?', "?param1=value1");
}
Ответ 6
Эта тема была настолько полезна, что я был вынужден поделиться своим решением. Ответ брата Вудрова был моей отправной точкой. Я также хотел обратить внимание на 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;
}
}
}
}
Ответ 7
Если вы ищете простое решение и не хотите сами делать кодирование, я бы рекомендовал использовать эту библиотеку:
https://www.npmjs.com/package/angular2-http-file-upload