Ionic-native: file.ReadAsText() doesn't resolve a promise

Created on 22 Dec 2019  路  20Comments  路  Source: ionic-team/ionic-native

I'm submitting a ... (check one with "x")
[x ] bug report
[ ] feature request
[ ] support request => Please do not submit support requests here, use one of these channels: https://forum.ionicframework.com/ or https://ionicworldwide.herokuapp.com/

Current behavior:
file.readAsText returns nothing, but only when ionic app has built in production mode and deployed on Android device.

Expected behavior:
It's expected that this function must return a string with file content.

Related code:

this.file.readAsText(this.file.dataDirectory, 'myapp.log').then((logContent) => {
console.log('resolved', logContent);
resolve(logContent)
}).catch((errRes) => {
console.log('rejected', errRes);
reject(errRes);

Other information:
I can see in Android Studio that my file exists and I can download it from the device.
Moreover, I can see in Studio debugger in Java code, that this function is executed, result is not empty, but nothing returns to JS code:

public void readFileAs(final String srcURLstr, final int start, final int end, final CallbackContext callbackContext, final String encoding, final int resultType) throws MalformedURLException {
try {
LocalFilesystemURL inputURL = LocalFilesystemURL.parse(srcURLstr);
Filesystem fs = this.filesystemForURL(inputURL);
if (fs == null) {
throw new MalformedURLException("No installed handlers for this URL");
}

        fs.readFileAtURL(inputURL, start, end, new Filesystem.ReadFileCallback() {
            public void handleData(InputStream inputStream, String contentType) {
                try {
                    ByteArrayOutputStream os = new ByteArrayOutputStream();
                    final int BUFFER_SIZE = 8192;
                    byte[] buffer = new byte[BUFFER_SIZE];

                    for (;;) {
                        int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE);

                        if (bytesRead <= 0) {
                            break;
                        }
                        os.write(buffer, 0, bytesRead);
                    }

                    PluginResult result;
                    switch (resultType) {
                    case PluginResult.MESSAGE_TYPE_STRING:
                        result = new PluginResult(PluginResult.Status.OK, os.toString(encoding));
                        break;

Ionic info: (run ionic info from a terminal/cmd prompt and paste output below):

` Ionic CLI : 5.4.5
Ionic Framework : @ionic/angular 4.11.7
@angular-devkit/build-angular : 0.803.21
@angular-devkit/schematics : 8.3.21
@angular/cli : 8.3.21
@ionic/angular-toolkit : 2.1.1

Capacitor:

Capacitor CLI : 1.4.0
@capacitor/core : 1.4.0

Cordova:

Cordova CLI : 9.0.0 ([email protected])
Cordova Platforms : android 8.1.0, browser 6.0.0
Cordova Plugins : cordova-plugin-file 6.0.2

Utility:

cordova-res : not installed
native-run : 0.2.9

System:

Android SDK Tools : 26.1.1
NodeJS : v13.0.1
npm : 6.13.2
OS : Windows 10

Most helpful comment

It seems like I was able to find the source of the issue.
The ionic-native/file API looks at the fileReader.on*, meanwhile, the file reader itself, when tries to deal with the file, works with the filereader._realReader. One thing is that you also need to change result to _result and error to _error for some unknown reason. I've really spent a ton of time looking into this. I'm currently going to apply a patch to see how it goes, but if any of you feel like it could be a real fix, I would be glad to submit a PR.
That's the code you'd like to have in a very weird location(@ionic-native/file/__ivy_ngcc/ngx/index.js) in the File.prototype.readFile

                reader._realReader.onloadend = function () {
                    if (reader._result != null) {
                        resolve(reader._result);
                    }
                    else if (reader._error != null) {
                        reject(reader._error);
                    }
                    else {
                        reject({ code: null, message: 'READER_ONLOADEND_ERR' });
                    }
                };

All 20 comments

Same issue.
Did you find any solution ?

Same issue.
Did you find any solution ?

Not yet, unfortunately...

I tried the solution mentioned in [https://forum.ionicframework.com/t/file-readastext-never-resolves/85375/2]
So just put polyfills.js before cordova.js in your index.html

There's no polyfill.js or cordova.js in my src/index.html. Where is this file exactly?

Hi there, having the same issue, also combining ionic native file with capacitor because capacitor does not yet support writing blobs.
I'm thinking of using it for just this reason, although it would be a very unclean solution.

There's no polyfill.js or cordova.js in my src/index.html. Where is this file exactly?

I'm using ionic v3 that why am able to change the order of polyfills.js inside my index.html.
In your v4 app I guess you can update it inside the angular.json file (but am not sure 馃槙)

Sadly no, the only way to change the file order would probably be to use a custom webpack build, but I don't really have time to learn how to do that. I decided to use Filesystem.readFile for this, which worked for android. However, on iOS, I have a problem.
Cordova's dataDirectory and Capacitor's FilesystemDirectory.DATA are apparently two different locations on ios. I have no simple way to check what the actual path of the capacitor variant is because there's no way to log it and it isn't in the docs. This is a real dilemma for me right now and any workaround would be highly appreciated.

Still very relevant.
My specific issue is about readAsBinaryString(). As a workaround on Android I've used a similar approach to @psanty's:

  • Store binary file with file.writeFile()
  • Read binary file with Capacitor's Filesystem.readFile() without passing the encoding parameter in order to read data as binary and receive a base64-encoded string
  • Decode base64-encoded string into binary string with atob()

Hi everyone,
thanks @elenche for the tip, that might have also worked - but I actually managed to solve this! I went on with the workaround, using cordova file to write and capacitor FileSystem to read. What I discovered was that there actually was a way to find out what FilesystemDirectory.DATA points to: the Filesystem.getUri function. I had simply missed it becaus the capacitor doc's top nav had blocked my view. It turns out that FilesystemDirectory.DATA points to the /documents folder on ios, not the data directory. This may be a bug.
So I just wrote the files into that directory and then was able to read it out without a problem.
Hope this helps!

Hi everyone,
I had the same problem with another method of this plugin: readAsBinaryString with Capacitor 2.1.0 and ionic 5.22.0.

Following the @NajiLouisAct solution, I simply moved up Pollyfills before index.html on build options inside the angular.json.
Before:

"options": {
            "aot": true,
            "preserveSymlinks": true,
            "outputPath": "dist/project",
            "index": "project/src/index.html",
            "main": "project/src/main.ts",
            "polyfills": "project/src/polyfills.ts",
[...]

After:

 "options": {
            "aot": true,
            "preserveSymlinks": true,
            "outputPath": "dist/project",
            "polyfills": "project/src/polyfills.ts",
            "index": "project/src/index.html",
            "main": "project/src/main.ts",

and it did the trick for me !
Hope this will works for you too.

I have the same problem. The above did not work for me. I have Ionic 5. This is the only way I've been able to get it to work:

readText(){
      let res;
      res = this.file.readAsText(this.fileDir, this.filename)
      .then((resp) => {
        return resp;
      }).catch((err) =>
      {
        return "";
      });
      return res;
  };

and then:

let text = "";
this.readText()
    .then((value) => {
            this.text = value;
      })
     .catch((err) => {
           console.log(err);
      });

very weird but it worked for me.

On the Capacitor and Ionic 5, sadly none of the work arounds mentioned here worked for me with readAsDataURL.

Suddenly this function started to work on some Android10 devices...

Hi not working on android 10 but working on android 7.1, any solution?

Same here with latest versions: Ionic 5, Capacitor 2.4, Cordova File 6.0, Ionic Native File 5.28 :/

Making a local AJAX call seems to be the simplest solution. You can use Capacitor.convertFileSrc which turns the path to a URI you can use. Turns out something like this:

const path = Capacitor.convertFileSrc(file.uri);
const response = await fetch(path);
const rawData = await response.json();

Tested on files roughly 5MB in size, works fine
Hope this helps someone

This seems related to #978, #505 and ionic-team/capacitor#1564

Why a 4 years old issue like this with a simple fix still isn't dealt with ?

Is the fix sustainable ?

It seems like I was able to find the source of the issue.
The ionic-native/file API looks at the fileReader.on*, meanwhile, the file reader itself, when tries to deal with the file, works with the filereader._realReader. One thing is that you also need to change result to _result and error to _error for some unknown reason. I've really spent a ton of time looking into this. I'm currently going to apply a patch to see how it goes, but if any of you feel like it could be a real fix, I would be glad to submit a PR.
That's the code you'd like to have in a very weird location(@ionic-native/file/__ivy_ngcc/ngx/index.js) in the File.prototype.readFile

                reader._realReader.onloadend = function () {
                    if (reader._result != null) {
                        resolve(reader._result);
                    }
                    else if (reader._error != null) {
                        reject(reader._error);
                    }
                    else {
                        reject({ code: null, message: 'READER_ONLOADEND_ERR' });
                    }
                };

Using a combination of...

  • Putting polyfills.js before cordova.js in index.html
  • Creating reader from *._realReader or new FileReader based on file instanceof Blob
  • Listening on *._realReader when new FileReader was used

...finally cracked it for me. Working on both Android and iOS. Putting six hours into this solution sounds like I actually didn't have it as bad as some, so thank you all!

Any updates or any solution for this?

Was this page helpful?
0 / 5 - 0 ratings