Capacitor: FileReader API not firing

Created on 23 May 2019  路  26Comments  路  Source: ionic-team/capacitor

Description of the problem:
In my project I'm trying to read and convert a Blob downloaded from the dropbox api to a text string. When building my project using Cordova, the FileReader fires onload and onprogress events. When building using Capacitor, filereader does not fire any events at all.

Affected platform

  • [ ] Android
  • [x ] iOS
  • [ ] electron
  • [ ] web

OS of the development machine

  • [ ] Windows
  • [ x] macOS
  • [ ] linux

Other information:
As suggested in other bug reports, I tried wrapping the FileReader in

platform.ready().then(_ => {
/// perform file reading 
})

and in

zone.run(() => {
/// perform file reading
})

None helped. Cordova has no issues reading the file.

When I use

var text = await (new Response(response.fileBlob)).text();

to read the file blob, it does work.

Capacitor version: 1.0.0-beta.24

Most helpful comment

You can try this.

export function getFileReader(): FileReader {
    const fileReader = new FileReader();
    const zoneOriginalInstance = (fileReader as any)["__zone_symbol__originalInstance"];
    return zoneOriginalInstance || fileReader;
}
...
let newInstance = getFileReader();

All 26 comments

Can you provide a sample project?

Okay, so in the sample project it works. Guess the problem lies somewhere in the use of dropbox combined with promises and the filereader. I shall investigate further and when I find something I'll let you know!

@jcesarmobile, apparently the problem is in the cordova-plugin-file plugin.
https://github.com/ronwondaal/capacitorFileReaderTest

But on the plugin itself or only when using it with Capacitor?

When using it with capacitor it prevents FileRead from firing events.

Looks like some problem with zone.js, but not sure why it only affects Capacitor and not Cordova

If I log the FileReader object I get this results:

Capacitor with Ionic {"__zone_symbol__originalInstance":{"_readyState":1,"_error":null,"_result":null,"_progress":0,"_localURL":"","_realReader":{}}}

Cordova with Ionic {"_readyState":1,"_error":null,"_result":null,"_progress":0,"_localURL":"","_realReader":{"__zone_symbol__originalInstance":{}}}

Capacitor without framework {"_readyState":1,"_error":null,"_result":null,"_progress":0,"_localURL":"","_realReader":{}}

Cordova without framework {"_readyState":1,"_error":null,"_result":null,"_progress":0,"_localURL":"","_realReader":{}}

The onload is being added to the FileReader object, but the problem is, when using Capacitor with zone.js, the FileReader object is put inside the "__zone_symbol__originalInstance" object and the onloadis set to the object containing the "__zone_symbol__originalInstance" and not on the real FileReader.

If we take zone.js (angular/ionic) out of the equation it works fine.

Found this ionic-native issue https://github.com/ionic-team/ionic-native/issues/505

Now I know why this isn't an issue on Cordova, polyfills.js is loaded there before cordova.js, but in Capacitor cordova.js is injected into the webview before polyfills.js.
I've tried injecting polyfills.js too but that doesn't seem to work.

Yeah, I took a look at the angular zone.js file, and it seems the have done some bug fixes regarding cordova. The also have zone.js patch for cordova here: https://github.com/angular/zone.js/blob/master/dist/zone-patch-cordova.js
But I'm not that skilled a programmer to be able to figure out what they are doing to patch the stuff :(

@jcesarmobile
Is someone looking for a fix? Currently also running into this issue while switching from cordova to capacitor...

Edit: this worked for me https://github.com/ionic-team/ionic-native/issues/505#issuecomment-503316333

Found this ionic-native issue ionic-team/ionic-native#505

Now I know why this isn't an issue on Cordova, polyfills.js is loaded there before cordova.js, but in Capacitor cordova.js is injected into the webview before polyfills.js.
I've tried injecting polyfills.js too but that doesn't seem to work.

@jcesarmobile, thanks for the tip. Loading polyfills.js before cordova.js fixed the issue for me.

EDIT: Loading pollyfills.js before cordova.js fixed this problem, but caused other problems. The best solution was to load pollyfills.js AFTER cordava.js, but BEFORE cordova_plugins.js (and all the other cordova plugin javascript files), which get injected into the Webview somewhere near the bottom of the element.

You can try this.

export function getFileReader(): FileReader {
    const fileReader = new FileReader();
    const zoneOriginalInstance = (fileReader as any)["__zone_symbol__originalInstance"];
    return zoneOriginalInstance || fileReader;
}
...
let newInstance = getFileReader();

I can confirm @tripodsgames 's solution is working for me.

Thank you for the workaround @tripodsgames saved me.

Maybe time for an official fix?

You can try this.

export function getFileReader(): FileReader {
    const fileReader = new FileReader();
    const zoneOriginalInstance = (fileReader as any)["__zone_symbol__originalInstance"];
    return zoneOriginalInstance || fileReader;
}
...
let newInstance = getFileReader();

This solution's working for me too, but after using it, I can see that the UI's loading slower than before(it's now display the text "back" then change to the icon). Does anyone else get that bad peformance or just me?

@tripodsgames 's solution worked like charm. Thank you so much.

closing since this is a zone.js bug related to changing cordova-plugin-file's FileReader.
If you are affected by this, use the workaround proposed by tripodsgames

I have this problem with Ionic V4 and capacitor without the cordova-plugin-file, use just a FileReader object and the problem is still present.

If you use the hack, it works but there is a very bad performance, i made tests in a galaxy s7 edge:
to have better performance i found this solution, use the NgZone:

```
import { NgZone } from "angular/core";

constructor(private zone:NgZone) {}

this.zone.run(()=>{

  const reader = new FileReader();
  reader.onload = () => { do stuffs with images };

});
``

You can try this.

export function getFileReader(): FileReader {
    const fileReader = new FileReader();
    const zoneOriginalInstance = (fileReader as any)["__zone_symbol__originalInstance"];
    return zoneOriginalInstance || fileReader;
}
...
let newInstance = getFileReader();

I have been hitting my head for almost a week now over this issue using Ionic 6, and finally this solution came to the rescue.

You can try this.

export function getFileReader(): FileReader {
    const fileReader = new FileReader();
    const zoneOriginalInstance = (fileReader as any)["__zone_symbol__originalInstance"];
    return zoneOriginalInstance || fileReader;
}
...
let newInstance = getFileReader();

You saved my life bro, thanks a ton

You can try this.

export function getFileReader(): FileReader {
    const fileReader = new FileReader();
    const zoneOriginalInstance = (fileReader as any)["__zone_symbol__originalInstance"];
    return zoneOriginalInstance || fileReader;
}
...
let newInstance = getFileReader();

You saved someone's else life here as well, Thanks man I waste a lot of time on this.

@tripodsgames Thanks!!!

Adding a few more lines - FileReader onload not geeting called in android SOLVED

export function getFileReader(): FileReader {
const fileReader = new FileReader();
const zoneOriginalInstance = (fileReader as any)["__zone_symbol__originalInstance"];
return zoneOriginalInstance || fileReader;
}

onSelectFile(event) {
if (event.target.files && event.target.files[0]) {
let newInstance = getFileReader();
newInstance.readAsDataURL(event.target.files[0]);
newInstance.onload = (imgsrc) => {
let url = (imgsrc.target as FileReader).result;
console.log(url)
}
}
}

If you have plugins that rely on FileReader working correctly, using snippets from the getFileReader() code mentioned above, I have a solution

Add to app.module.ts:

export class FileReaderA extends window.FileReader {
    constructor() {
        super();
        const zoneOriginalInstance = (this as any)['__zone_symbol__originalInstance'];
        return zoneOriginalInstance || this;
    }
}

window.FileReader = FileReaderA;

or add the above code to another file and import the file, not the class.

This is a hack but it means that when calling new FileReader() all the events work. Ideally it would be nice to be able to control the order in which scripts are imported. As the fix I've always used in a cordova based project is to change the order of polyfills.js and cordova.js in the index.html file.

private getFileReader(): FileReader {
const fileReader = new FileReader();
const zoneOriginalInstance = (fileReader as any)["__zone_symbol__originalInstance"];
return zoneOriginalInstance || fileReader;
}
...
let newInstance = getFileReader();
```

Worked for me after days searching for a solution! Tks @tripodsgames !

You can try this.

export function getFileReader(): FileReader {
    const fileReader = new FileReader();
    const zoneOriginalInstance = (fileReader as any)["__zone_symbol__originalInstance"];
    return zoneOriginalInstance || fileReader;
}
...
let newInstance = getFileReader();

It's working to me. Thanks a lot!

If you have plugins that rely on FileReader working correctly, using snippets from the getFileReader() code mentioned above, I have a solution

Add to app.module.ts:

export class FileReaderA extends window.FileReader {
  constructor() {
      super();
      const zoneOriginalInstance = (this as any)['__zone_symbol__originalInstance'];
      return zoneOriginalInstance || this;
  }
}

window.FileReader = FileReaderA;

or add the above code to another file and import the file, not the class.

This is a hack but it means that when calling new FileReader() all the events work. Ideally it would be nice to be able to control the order in which scripts are imported. As the fix I've always used in a cordova based project is to change the order of polyfills.js and cordova.js in the index.html file.

Thanks to you and @tripodsgames the fix saved me, took me 4 days to finally come across this solution and it works!

Thanks @tripodsgames
Your solution saved my day

Was this page helpful?
0 / 5 - 0 ratings