Angular 与可观测数据并行读取文件
我正在编写一些应用程序来读取用户选择的文件并将其转换为base64。我想在内存中读取所有文件时收到通知。为此,我使用Angular 与可观测数据并行读取文件,angular,typescript,rxjs5,Angular,Typescript,Rxjs5,我正在编写一些应用程序来读取用户选择的文件并将其转换为base64。我想在内存中读取所有文件时收到通知。为此,我使用Observable来处理FileReader的onload事件,并发送完整的通知。我正在使用forkJoin并行运行该操作 请看下面的代码,我正在创建Observable并订阅它 onChange($event: any) { console.log('No of files selected: ' + $event.target.files.length); var o
Observable
来处理FileReader
的onload
事件,并发送完整的通知。我正在使用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!!!");
}
);
}
完整的示例代码在中提供
当我选择一个文件时,会执行onload
函数,并获得以下控制台日志
但是,当我选择多个文件时,onload
只执行一次,批处理操作没有完成。请参阅以下控制台日志
有人能帮我理解我犯了什么错误吗?我从一个类似的问题中找到了答案。显然,这与循环和回调的运行顺序有关。我认为,.forkJoin()
正在等待传递给它的所有可观察对象完成,但当它获得所有可观察对象并订阅时,第一次onload已经完成,因此可观察到的完成永远不会发生
无论如何,您可以通过将设置FileReader、Observable和onload回调的代码放入它自己的函数中来解决这个问题
我使用flatmap来确保加载了所有内容
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
}
从'@angular/core'导入{Injectable}
从“./attachments.component”导入{Attachment}
从“@angular/core”导入{Inject}
从'rxjs/BehaviorSubject'导入{BehaviorSubject}
从“rxjs/Observable”导入{Observable};
从“./attachment.backend.service”导入{AttachmentBackendService}
导入“rxjs/add/observable/from”
导入“rxjs/add/operator/mergeMap”
@可注射()
导出类附件存储{
私有_附件:BehaviorSubject=新的BehaviorSubject([])
专用数据存储:{
附件:附件[]
}
私有存储ID:字符串=“”
私有附件ID:编号=0
构造函数(私有attachmentBackendService:attachmentBackendService){
this.dataStore={attachments:[]}
}
获取附件(){
返回此。\u attachments.asObservable()
}
//公开的
添加文件(文件:文件列表){
让fileArray=Array.from(文件)
此文件为.processFiles(
fileArray[0],
fileArray.slice(1))
.订阅(
(附件)=>{
此.storeAndSaveAttachment(附件)
this.\u attachments.next(this.dataStore.attachments)
},
(e) =>{
控制台日志(e)
},
() => {
console.log(“文件加载完成!!!”)
})
返回此.storeId
}
removeFile(索引:编号){
让attachment=this.dataStore.attachments[index]
this.attachmentBackendService.deleteAttachment(this.storeId,attachment.id)
this.dataStore.attachments.splice(索引,1)
this.\u attachments.next(this.dataStore.attachments)
}
//私人的
私有进程文件(文件:文件,文件数组:文件[]){
如果(fileArray.length>0){
返回此.processFiles(
fileArray.slice(0,1)[0],
fileArray.slice(1))
.flatMap((附件)=>{
此.storeAndSaveAttachment(附件)
返回此.fileReaderObs(文件,this.attachmentId++)
})
}否则{
if(this.storeId=='')
{
this.storeId=this.attachmentBackendService.storeId
}
返回此.fileReaderObs(文件,this.attachmentId++)
}
}
private storeAndSaveAttachment(附件:附件){
this.dataStore.attachments.push(附件)
this.attachmentBackendService.saveAttachment(this.storeId,附件)
}
私有文件阅读器(文件:file,附件ID:number){
let reader=new FileReader()
让fileReaderObs=Observable.create((observator:any)=>{
reader.onload=函数(){
让附件:附件={
id:attachmentId,
name:file.name,
数据:btoa(reader.result)
}
下一位观察员(附件)
观察员:完成()
}
})
reader.readAsBinaryString(文件)
返回文件读取器
}
}我想提出这个解决方案。如果这个解决方案有问题,请随时告诉我
const files=Array.from(event.srcielement.files);
可观察。从(文件)
.map((文件:file)=>{
const reader=new FileReader();
const load$=Observable.fromEvent(读取器,'load')。取(1);
const read$=Observable.of(file).do(reader.readAsDataURL.bind(reader));
返回[load$,read$];
})
.toArray()
.switchMap((值:任意)=>{
const arrayObservables=值。reduce((acc,value)=>acc.concat(value),[]);
返回可观察的forkJoin(…arrayObservables);
})
.订阅({
下一步:console.log
});代码>-1表示非标准的readAsBinaryString,-1表示不处理错误,-1表示有状态处理,-1表示使用subscribe而不是mergeMap或类似。。。等等,我确实让你知道了你的错误。但是我没有给你-1分,因为你已经尽力了;)。需要对您的答案进行太多更改,以使其成为生产质量代码,以便我对其进行评论或更正。新的FileReader(文件)不正确,因为FileReader不使用参数进行实例化。
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
}