web-dev-qa-db-ja.com

Angular2を使用したREST AP​​Iへのファイルのアップロード

実際、Spring REST AP​​IとAngular 2でコーディングされたインターフェースを使用しています。

私の問題は、Angular 2のファイルをアップロードできないことです。

JavaのWebリソースは次のとおりです。

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

そして、AuthヘッダーなどでURLリクエストを介して呼び出すと完全に機能します...(ChromeのAdvanced Rest Client拡張を使用)

証明:(その場合はすべて正常に動作します)

enter image description here を追加しました

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

私のWebサービスは、Tomcatログにエラー500を返します。 http://Pastebin.com/PGdcFUQb

'Content-Type': undefinedメソッドも試しましたが、成功しませんでした(その場合、Webサービスは415エラーを返します。

誰かが問題が何であるかを理解するのを手伝ってくれますか?

問題は解決しました。後でそのコードをコードで更新します:)ありがとう。

49
Slater

実際、これは最終リリースで非常に簡単です。私が出くわしたほとんどの情報は時代遅れであるため、頭を包むのに少し時間がかかりました。他の誰かがこれに苦労している場合に備えて、ここに私のソリューションを投稿します。

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をサポートする必要がある場合は、ポリフィルが必要です。

2017-01-07更新

複数のファイルのアップロードを処理できるようにコードを更新しました。また、私の元の答えには、FormDataに関するかなり重要な部分がありませんでした(実際のアップロードロジックを自分のアプリの別のサービスに移動したため、そこで処理していました)。

156
Brother Woodrow

実際、現時点では、Angular2 HTTPサポートのpostput、およびpatchメソッドにのみ文字列入力を提供できます。

これをサポートするには、以下で説明するように、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があります。

26

これは私のために働いています:

<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)
        )
}}
12
Anil Samal

これは私のために働いています: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
heman123

このスレッドはとても役に立ちましたので、私のソリューションを共有することを余儀なくされました。 Brother Woodrow の答えが私の出発点でした。 Rob Gwynn-Jones 'comment "Content-Typeヘッダーを手動で設定しないように注意してください"これは非常に重要で、時間を大幅に節約してくれました。


このバージョンでは、すべてのファイルを一度にアップロードする前に、(異なるフォルダーから)複数の追加/削除操作が可能です。

(異なるフォルダから)同じ名前の複数のファイルを一緒にアップロードできますが、同じファイルをアップロードリストに2回追加することはありません(これは見かけほど簡単ではありません!)。

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

'addFiles'のコールバックにより、コンポーネントの外部でアップロードを実行できます。コンポーネントは次のように使用されます。

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

「setFiles」はコールバックです。このコンテキストの「this」は親コンポーネントです。

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

残っているのは、アップロードAPIを呼び出す前に、マルチパートペイロードをアタッチすることです(親コンポーネントでも):

const formData = new FormData();
            
for (let i = 0; i < this.files.length; i ++) {

    formData.append('file[]', this.files[ i ]);
}

これが参考になり、必要に応じて修正/更新できることを願っています。乾杯!

2
vanwinter

シンプルなソリューションを探していて、自分でコーディングしたくない場合は、このライブラリを使用することをお勧めします。

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

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

次に:

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