cordova-plugin-file-transfer icon indicating copy to clipboard operation
cordova-plugin-file-transfer copied to clipboard

`download` method failing on cordova / android platform with `java.lang.NullPointerException` exception

Open the-other-sunny opened this issue 11 months ago • 6 comments

Hi ! I'm currently unable to make the plugin work properly. FileTransfer.download is failing with an error code 3 (FileTransferError.CONNECTION_ERR) and an exception java.lang.NullPointerException.

Minimal example

cordova create . com.example.my_app "My App"
cordova platform add android
cordova plugin add cordova-plugin-file-transfer

Add this child to the widget element in config.xml.

<access origin="*" />

Add a call to the demo function below from within onDeviceReady.

function demo() {
    window.requestFileSystem(window.PERSISTENT, 5 * 1024 * 1024,
        fs => fs.root.getFile('cordova_bot.png', { create: true, exclusive: false },
            fileEntry => ft_approach(fileEntry),
            error => console.log(`file creation error: ${error}`)
        ),
        (error) => console.log(`fs loading error: ${error}`)
    );
}

function ft_approach(fileEntry) {
    const fileTransfer = new FileTransfer();
    
    fileTransfer.download(
        'https://cordova.apache.org/static/img/cordova_bot.png',
        fileEntry.toURL(),
        () => window.alert('download complete'),
        error => console.log(`download error: ${JSON.stringify(error, undefined, 2)}`),
        false,
    );
}

Run the app on the Android Studio emulator and attach Chrome DevTools . The output should be something like:

download error: {
  "code": 3,
  "source": "https://cordova.apache.org/static/img/cordova_bot.png",
  "target": "https://localhost/__cdvfile_persistent__/cordova_bot.png",
  "http_status": 200,
  "body": null,
  "exception": "java.lang.NullPointerException"
}

Workaround

Using web APIs. Replacing ft_approach with the xhr_approach function below works, although not idea for large files.

function xhr_approach(fileEntry) {
    const xhr = new XMLHttpRequest();

    xhr.open('GET', 'https://cordova.apache.org/static/img/cordova_bot.png', true);
    xhr.responseType = 'blob';
    xhr.onload = function () {
        const blob = xhr.response;
        if (xhr.status != 200) {
            console.error(`request error ${xhr.status}: ${xhr.statusText}`);
            return;
        }
        fileEntry.createWriter(function (fileWriter) {
            fileWriter.onwriteend = () => window.alert('download complete');
            fileWriter.onerror = error => console.log(`file writing error: ${error}`);
            fileWriter.write(blob);
        });
    };
    xhr.onerror = function() {
        console.log('request error')
    }
    xhr.send();
}

Note: Update CSP accordingly

<meta http-equiv="Content-Security-Policy" content="default-src https://cordova.apache.org ...">

Versions

Windows 10 Home
Node 18.17.1
Cordova 12.0.0
cordova-android 12.0.1
cordova-plugin-file 8.0.0
cordova-plugin-file-transfer 2.0.0
Android SDK 33
Gradle 8.3

the-other-sunny avatar Sep 14 '23 16:09 the-other-sunny

@the-other-sunny, Hello, did you find any solution?

wilywork avatar Nov 13 '23 16:11 wilywork

@the-other-sunny, Hello, did you find any solution?

the xhr approach ^^

the-other-sunny avatar Nov 14 '23 00:11 the-other-sunny

@the-other-sunny can you explain it a bit more, may be with some code. I am facing the same issue

anubhavs19 avatar Feb 28 '24 11:02 anubhavs19

Hello, I got the same error

03-25 23:08:57.066 29040 29835 E FileTransfer: {"code":3,"source":"https:\/\/XXXX","target":"cdvfile:\/\/localhost\/persistent\/path\/to\/file.txt","http_status":200,"exception":"java.lang.NullPointerException"}
03-25 23:08:56.746 29040 29835 E FileTransfer: java.lang.NullPointerException
03-25 23:08:56.746 29040 29835 E FileTransfer:  at java.io.FileOutputStream.<init>(FileOutputStream.java:227)
03-25 23:08:56.746 29040 29835 E FileTransfer:  at java.io.FileOutputStream.<init>(FileOutputStream.java:186)
03-25 23:08:56.746 29040 29835 E FileTransfer:  at org.apache.cordova.filetransfer.FileTransfer$2.run(FileTransfer.java:796)
03-25 23:08:56.746 29040 29835 E FileTransfer:  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
03-25 23:08:56.746 29040 29835 E FileTransfer:  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)

did someone find a solution ?

I'm on cordova-android 12

Regards Louis

lchanouha avatar Mar 25 '24 22:03 lchanouha

@anubhavs19 @lchanouha I presented a workaround in my initial message which is to use the browser/webview's XMLHttpRequest API. I'm sorry but I'm not aware of a native solution and also not well versed in cordova.

the-other-sunny avatar Mar 25 '24 23:03 the-other-sunny

Hi,

the following code is not working on Android..

var example_external_file = "https://cordova.apache.org/static/img/cordova_bot.png"

window.requestFileSystem(window.PERSISTENT, 128 * 1024 * 1024, function (fs) {
  fs.root.getFile("cordova_bot.png", {create: true}, function (fileEntry) {
    var target = fileEntry.toURL();
    cordova.fileTransfer.download(example_external_file, target, function (success) {
      console.log('success', success);
    }, function (err) {
      console.log('error', err);
    })
  });
});

err is:

err = {
  body: null,
  code: 3,
  exception: "java.lang.NullPointerException",
  http_status: 200,
  source: "https://cordova.apache.org/static/img/cordova_bot.png",
  target: "https://localhost/__cdvfile_persistent__/cordova_bot.png"
};

the problem is the target path. is should be something with file:// and not https:// the problem maybe lies here https://github.com/apache/cordova-plugin-file/issues/619

With https:// fails this code in Cordova-Plugin-File

file = resourceApi.mapUriToFile(targetUri); targetUri = "https://localhost/cdvfile_persistent/cordova_bot.png" file = NULL

and file=NULL leads to java.lang.NullPointerException

Quickfix: fileEntry.toURL() returns: "https://localhost/cdvfile_persistent/cordova_bot.png" -> wrong

fileEntry.toNativeURL() also returns: "https://localhost/cdvfile_persistent/cordova_bot.png" -> wrong

fileEntry.nativeUrl is "file:///data/user/0/de.fuf.corodva-app/files/files/cordova_bot.png" -> fine

Workaround: use fileEntry.nativeUrl instead of fileEntry.toUrl() as target in cordova.fileTransfer.download(source, target)

Code with workaround:

window.quirksMode = false;

function download_file(source, fileEntry, onSuccess, onError) {
  var target = fileEntry.toURL();
  // if quirksMode is enabled, use the nativeURL instead of fileEntry.toURL
  if (window.quirksMode) {
    target = fileEntry.nativeURL;
  }
  cordova.fileTransfer.download(source, target, onSuccess, function (err) {
    // only if the error appears and quirksMode is false
    if (err.code === 3 && window.quirksMode === false) {
      // enable quirksMode
      window.quirksMode = true;
      console.log('error', err, '-> enabling quirksMode')
      // and try to redownload the file..
      download_file(source, fileEntry, onSuccess, onError);
      return;
    }
    onError(err);
  });
}

window.requestFileSystem(window.PERSISTENT, 128 * 1024 * 1024, function (fs) {
  fs.root.getFile("cordova_bot.png", {create: true}, function (fileEntry) {
    download_file(example_external_file, fileEntry, function (succ) {
      console.log("success", succ);
    }, function (err) {
      console.log("error", err);
    });
  });
});

fuf-nf avatar Apr 22 '24 11:04 fuf-nf