n00*_*dl3 3 filereader cordova zone.js angular
我有一个Angular 4.3 + Cordova应用程序,以前工作得很好.但现在,我在应用启动时出现了一个空白屏幕,没有任何事情发生.
经过一段时间的挖掘,我意识到它的来源:
我的主页受到保护的保护,该CanActivate保护将检查一些文件系统持久化的首选项,并将用户重定向到另一个页面(如果这是第一次运行或缺少必需的首选项),以填写所需的属性.
所以应用程序的推出取决于我的CanActivate后卫,这取决于我PreferenceService自己取决于FileSystemService我自己实现的.问题是当我尝试读取存储用户首选项的文件时,没有触发单个回调,没有任何反应,甚至没有错误.
这是我的一部分FileSystemService失败没有任何错误:
read(file: FileEntry, mode: "text" | "arrayBuffer" | "binaryString" | "dataURL" = "text"): Observable<ProgressEvent> {
return this.cdv.ready.flatMap(() => {
return Observable.create(observer => {
file.file(file => {
let reader = new FileReader();
reader.onerror = (evt: ErrorEvent) => {
this.zone.run(() => observer.error(evt)); //never triggered
};
reader.onload = (evt: ProgressEvent) => {
this.zone.run(() => observer.next(evt)); //never trigerred
};
switch (mode) {
case "text":
reader.readAsText(file);
break;
case "arrayBuffer":
reader.readAsArrayBuffer(file);
break;
case "binaryString":
reader.readAsBinaryString(file);
break;
case "dataURL":
reader.readAsDataURL(file);
break;
}
});
});
});
}
Run Code Online (Sandbox Code Playgroud)
为什么会发生这种情况,如何解决这个问题,以便我的回调被触发?
在调试此代码时,我意识到zone.js/zone-patch-cordova构造函数已被cordova和zone.js修补.从我的理解有关zone.js补丁是改变每一个"onProperty"( ,FileReader,onload)onloadend它的onerror对应物.
模块名称:
on_property
zone.js的行为:
addEventListener(...)将成为区域意识target.onProp
但是cordova没有使用target.addEventListener(prop)API来通知侦听器操作已经结束.
一种解决方案可能是dispatchEvent(...)从zone.js 停用模块,但它可能会破坏角度的行为.
所以这就是我应对这种情况的方式:
read(file: FileEntry, mode: "text" | "arrayBuffer" | "binaryString" | "dataURL" = "text"): Observable<ProgressEvent> {
return this.cdv.ready.flatMap(() => {
return Observable.create(observer => {
file.file(file => {
let FileReader: new() => FileReader = ((window as any).FileReader as any).__zone_symbol__OriginalDelegate
let reader = new FileReader();
reader.onerror = (evt: ErrorEvent) => {
this.zone.run(() => observer.error(evt)); //never triggered
};
reader.onload = (evt: ProgressEvent) => {
this.zone.run(() => observer.next(evt)); //never trigerred
};
switch (mode) {
case "text":
reader.readAsText(file);
break;
case "arrayBuffer":
reader.readAsArrayBuffer(file);
break;
case "binaryString":
reader.readAsBinaryString(file);
break;
case "dataURL":
reader.readAsDataURL(file);
break;
}
});
});
});
}
Run Code Online (Sandbox Code Playgroud)
这里的秘密是zone.js将原始构造函数保留在onProperty属性中,因此调用它实际上将__zone_symbol__OriginalDelegate直接调用Cordova ,而不使用zone.js补丁.
这个解决方案是一个肮脏的黑客,我在区域的存储库上打开了一个问题
有同样的问题FileReader(它内部调用一个FileWriter)所以我写了这个小垫片:
function noZonePatch(cb: () => void) {
const orig = FileReader;
const unpatched = ((window as any).FileReader as any).__zone_symbol__OriginalDelegate;
(window as any).FileReader = unpatched;
cb();
(window as any).FileReader = orig;
}
Run Code Online (Sandbox Code Playgroud)
然后将我的调用包装成读/写操作:
write(file: FileEntry, content: Blob) {
return this.cdv.ready.flatMap(() => {
return Observable.create((out: Observer<ProgressEvent>) => {
file.createWriter((writer) => {
noZonePatch(() => {
writer.onwrite = (evt: ProgressEvent) => {
this.zone.run(() => {
out.next(evt);
out.complete();
});
};
writer.onerror = (evt) => {
this.zone.run(() => out.error(evt));
};
writer.write(content); // this is where FileReader is called internally
})
}, err => out.error(err));
});
});
}
read(file: FileEntry, mode: ReadMode = "text"): Observable<ProgressEvent> {
return this.cdv.ready.switchMap(() => Observable.create((observer: Observer<ProgressEvent>) => {
file.file(file => {
noZonePatch(() => {
let reader = new FileReader();
reader.onerror = (evt: ErrorEvent) => {
this.zone.run(() => observer.error(evt));
};
reader.onload = (evt: ProgressEvent) => {
this.zone.run(() => observer.next(evt));
};
switch (mode) {
case "text":
reader.readAsText(file);
break;
case "arrayBuffer":
reader.readAsArrayBuffer(file);
break;
case "binaryString":
reader.readAsBinaryString(file);
break;
case "dataURL":
reader.readAsDataURL(file);
break;
}
});
});
}));
}
Run Code Online (Sandbox Code Playgroud)