2016-08-16 1 views
2

사용자가 선택한 파일을 읽고 base64로 변환하는 응용 프로그램을 작성 중입니다. 모든 파일이 메모리에서 읽힐 때 알림을 받고 싶습니다. 이 목적을 위해 이벤트를 FileReader으로 처리하고 완전한 알림을 보내는 곳에서 Observable을 사용하고 있습니다. 병렬로 작업을 실행하려면 forkJoin을 사용하고 있습니다.observables와 병렬로 파일 읽기

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!!!"); 
     } 
    ); 
} 

전체 샘플 코드는 내가 하나의 파일을 선택하면, onload 기능이 실행됩니다 plunkr

에서 사용할 수 있습니다 내가 선택했을 때 나는 다음과 같은 콘솔 로그

enter image description here

그러나

를 얻을 수 복수 파일 onload이 한 번만 실행되고 일괄 작업이 완료되지 않습니다. 내가 실수를하고 어디

enter image description here

누군가가 나를 이해하는 데 도움이 될 수 있습니다 다음과 같은 콘솔 로그를 참조하십시오?

답변

1

나는 비슷한 질문에서 this 대답을 찾았습니다. 분명히 그것은 루프와 콜백이 실행되는 순서와 관련이 있습니다. 나는 .forkJoin()이 많은 Observables가 완료 될 때까지 기다리고 있다고 생각하지만, 모든 Observable이 전달 된 시점까지 기다리고 있지만, 모든 요청을 수신하고 구독하면 첫 번째 onload가 이미 완료되어 Observable completion은 발생하지 않습니다.

어쨌든 FileReader, Observable 및 onload 콜백을 설정 한 코드를 자신의 함수에 넣으면이 문제를 해결할 수 있습니다. 나는 확실히 모든 것을 할 flatmap를 사용하여이 코드를 사용하고 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

비표준 인 readAsBinaryString의 경우 -1, 오류 처리를하지 않는 경우 -1, 상태 저장 처리의 경우 -1, mergeMap 대신 subscribe를 사용하는 경우 등 ... – Henrik

+0

내 실수를 확인하여 배울 수 있습니다. – progressdll

+0

나는 당신의 실수에 대해 당신에게 알렸다. 하지만 최선을 다했기 때문에 -1 점을주지 않았다.). 의견이나 수정을 위해 생산 품질의 코드로 만들기 위해 답을 수정해야하는 부분이 너무 많습니다. – Henrik

1

나는이 솔루션을 제안 할로드됩니다. 이 솔루션을 사용하는 데 어려움이 있으면 알려주세요.

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

건배.