Подтвердить что ты не робот

Загрузка файла с помощью 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 в этом случае.

Может кто-нибудь помочь мне разобраться, в чем проблема?

Проблема решена, я уточню этот вопрос позже с моим кодом:), но посмотрите на плункер, который работает отлично. Спасибо.

4b9b3361

Ответ 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