2016-08-16 5 views
2

私は、選択したファイルをユーザが読み込んでbase64に変換するアプリケーションを作成しています。私は、すべてのファイルがメモリに読み込まれたときに通知を出したい。この目的のために私はのイベントをFileReaderとして扱い、完全な通知を送るObservableを使用しています。私はforkJoinを使って操作を並行して実行しています。オブザーバブルと並行してファイルを読む

Observableを作成して購読しているコードを下に見てください。

onChange($event: any) { 
    console.log('No of files selected: ' + $event.target.files.length); 
    var observableBatch : any = []; 

    var rawFiles = $event.target.files; 
    for (var i = rawFiles.length - 1; i >= 0; i--) { 

     var reader = new FileReader(); 
     var file = rawFiles[i]; 
     var myobservable = Observable.create((observer: any) => { 
     reader.onload = function (e: any) { 
      var data = e.target; 
      var imageSrc = data.result; 
      console.log('File loaded succesfully.'); 
      observer.next("File loaded"); 
      observer.complete(); 
     }; 
     }); 

     observableBatch.push(myobservable); 
     reader.readAsArrayBuffer(file); 

    } 

    Observable.forkJoin(observableBatch) 
    .subscribe(
     (m) => { 
     console.log(m); 
     }, 
     (e) => { 
     console.log(e); 
     }, 
    () => { 
     console.log("All file(s) loading completed!!!"); 
     } 
    ); 
} 

完全なサンプルコードは、私は1つのファイルを選択すると、onload機能が実行されplunkr

で利用可能であり、私が選択したとき、私は、以下のコンソールログ

enter image description here

しかし

を取得します複数のファイルonloadが一度だけ実行され、バッチ処理が完了しません。以下のコンソールログに

enter image description here

を参照してください誰かが、私は間違いを犯していた場合、私が理解するのに役立つことはできますか?

答えて

1

見つかったthis同様の質問からの回答。明らかに、ループとコールバックが実行される順序と関係しています。私は.forkJoin()がそれに渡されたObservableが完了するのを待っていると思いますが、それらのすべてを取得して購読する時点で、最初のonloadはすでに終了しているため、Observable completionは起こりません。

とにかく、FileReader、Observable、およびonloadコールバックを設定するコードを独自の関数に入れることで、この問題を解決できます。 Here is the plunkr showing that it works.

export class AppComponent { 
    title = 'file reader'; 
    observableBatch : any = []; 

    onChange($event: any) { 
    console.log('No of files selected: ' + $event.target.files.length); 
    //Make sure to clear the observableBatch array before restarting the whole process. 
    this.observableBatch = []; 

    var rawFiles = $event.target.files; 
    for (var i = rawFiles.length - 1; i >= 0; i--) { 
     this.setUpFile(rawFiles[i]); 
    } 

    Observable.forkJoin(this.observableBatch) 
    .subscribe(
     (m) => { 
     console.log(m); 
     }, 
     (e) => { 
     console.log(e); 
     }, 
    () => { 
     console.log("All file(s) loading completed!!!"); 
     } 
    ); 
    } 


    setUpFile(file) { 
    var reader = new FileReader(file); 
    var myobservable = Observable.create((observer: any) => { 
     reader.onload = function (e: any) { 
     var data = e.target; 
     var imageSrc = data.result; 
     console.log('File loaded succesfully.'); 
     observer.next("File loaded"); 
     observer.complete(); 
     }; 
    }); 

    this.observableBatch.push(myobservable); 
    reader.readAsArrayBuffer(file); 
    } 
} 
+0

私のためには機能しません! –

+0

FileReaderがインスタンス化するための引数を取らないため、新しいFileReader(file)が正しくありません。 –

4

私はこの解決策を提案することを確認し、すべてが

import {Injectable} from '@angular/core' 
import {Attachment} from './attachments.component' 
import {Inject} from '@angular/core' 
import {BehaviorSubject} from 'rxjs/BehaviorSubject' 
import {Observable} from "rxjs/Observable"; 
import {AttachmentBackendService} from './attachment.backend.service' 
import 'rxjs/add/observable/from' 
import 'rxjs/add/operator/mergeMap' 

@Injectable() 
export class AttachmentStore { 
    private _attachments: BehaviorSubject<Attachment[]> = new  BehaviorSubject<Attachment[]>([]) 
    private dataStore : { 
    attachments : Attachment[] 
    } 
    private storeId : string = '' 
    private attachmentId : number = 0 

    constructor(private attachmentBackendService: AttachmentBackendService) { 
    this.dataStore = { attachments : [] } 
    } 

    get attachments() { 
    return this._attachments.asObservable() 
    } 

    // public 
    addFiles(files: FileList) { 
    let fileArray = Array.from(files) 
    this.processFiles(
     fileArray[0], 
     fileArray.slice(1)) 
     .subscribe(
     (attachment) => { 
      this.storeAndSaveAttachment(attachment) 
      this._attachments.next(this.dataStore.attachments) 
     }, 
     (e) => { 
      console.log(e) 
     }, 
     () => { 
      console.log("file loading completed!!!") 
     }) 
    return this.storeId 
} 

removeFile(index: number) { 
    let attachment = this.dataStore.attachments[index] 
    this.attachmentBackendService.deleteAttachment(this.storeId, attachment.id) 
    this.dataStore.attachments.splice(index, 1) 
    this._attachments.next(this.dataStore.attachments) 
} 

// private 
private processFiles(file : File, fileArray : File[]) { 
    if (fileArray.length > 0) { 
    return this.processFiles(
        fileArray.slice(0,1)[0], 
        fileArray.slice(1)) 
       .flatMap((attachment) => { 
        this.storeAndSaveAttachment(attachment) 
        return this.fileReaderObs(file,this.attachmentId++) 
       }) 
    } else { 
    if (this.storeId == '') 
    { 
     this.storeId = this.attachmentBackendService.storeId 
    } 
    return this.fileReaderObs(file,this.attachmentId++) 
    } 
} 

private storeAndSaveAttachment(attachment : Attachment) { 
    this.dataStore.attachments.push(attachment) 
    this.attachmentBackendService.saveAttachment(this.storeId, attachment) 
} 

private fileReaderObs(file : File, attachmentId : number) { 
    let reader = new FileReader() 
    let fileReaderObs = Observable.create((observer: any) => { 
    reader.onload = function() { 
     let attachment : Attachment = { 
     id : attachmentId, 
     name : file.name, 
     data : btoa(reader.result) 
     } 
     observer.next(attachment) 
     observer.complete() 
    } 
    }) 
    reader.readAsBinaryString(file) 
    return fileReaderObs 
} 

}

+1

-1は標準ではないreadAsBinaryStringに、-1はエラーを処理しないため-1はステートフルな処理に、-1はmergeMapの代わりにsubscribeを使用するなど... – Henrik

+0

私はそれらから学ぶことができるので、私の間違いを訂正してください – progressdll

+0

あなたの間違いを知らせました。しかし、あなたがベストを尽くしたので、私はあなたに-1ポイントを与えなかった。私がコメントしたり訂正したりするために、生産品質のコードにするためにあなたの答えに必要な変更が多すぎます。 – Henrik

1

をロードされていることを確認するためにflatmapを使用してこのコードを使用しています。あなたはこの解決策にいくつかの問題がある場合は、私に教えてください。

const files = Array.from(event.srcElement.files); 
 

 
Observable.from(files) 
 
    .map((file: File) => { 
 
    const reader = new FileReader(); 
 
    const load$ = Observable.fromEvent(reader, 'load').take(1); 
 
    const read$ = Observable.of(file).do(reader.readAsDataURL.bind(reader)); 
 
    return [load$, read$]; 
 
    }) 
 
    .toArray() 
 
    .switchMap((values: any) => { 
 
    const arrayObservables = values.reduce((acc, value) => acc.concat(value), []); 
 
    return Observable.forkJoin(...arrayObservables); 
 
    }) 
 
    .subscribe({ 
 
    next: console.log 
 
    });

乾杯。

関連する問題