androidcordovacordova-pluginsngcordovacordova-plugin-file

Cordova file plugin - save file in device


I'm having a lot of troubles saving files in Android.
The project is a hybrid application developed with Ionic with these plugins:

com.phonegap.plugins.fileopener 1.0.0 "File Opener"
com.telerik.plugins.nativepagetransitions 0.4.2 "Native Page Transitions"
cordova-plugin-compat 1.0.0 "Compat"
cordova-plugin-crosswalk-webview 2.0.0 "Crosswalk WebView Engine"
cordova-plugin-file 4.2.0 "File"
cordova-plugin-network-information 1.2.2-dev "Network Information"
cordova-plugin-whitelist 1.2.3-dev "Whitelist"
cordova-plugin-wkwebview-engine 1.0.4-dev "Cordova WKWebView Engine"
ionic-plugin-keyboard 2.2.1 "Keyboard"

Android platform version is 5.2.1
The device I'm using is a Samsung A7

This is an abstract from AndroidManifest.xml

<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Case1

if I try with this snippet (actually working on another project)

var storagePath = "/storage/emulated/0";
var fileDir = cordova.file.externalDataDirectory.replace(cordova.file.externalRootDirectory, '');
var fileName = $scope.ngDocument.documentId + ".pdf"
var filePath = storagePath + "/" + fileDir + fileName;
$cordovaFile.writeFile(filePath, BINARY_ARR, {'append': false}).then(function(result) {}, function(err) {});

I get {"code":5,"message":"ENCODING_ERR"} as Callback from the $cordovaFile.writeFile, no matter if I use absolute path, relative path, just the file name and no file is ever created.

Case2

With this snippet

window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {
  console.log('file system open: ' + fs.name);
  fs.root.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
    console.log("fileEntry:" + JSON.stringify(fileEntry));
    writeFile(fileEntry, BINARY_ARR);
  }, function(data){});
}, function(data){});

happen two different things

Case 2.1

If no config options are specified in config.xml the app creates an empty folder into /storage/emulated/0/Android/media/{myAPP}

Case 2.2

with these two preferences

<preference name="AndroidPersistentFileLocation" value="Compatibility" />
<preference name="AndroidExtraFilesystems" value="cache" />

a file in /storage/emulated/0 (external SSD) is created and in logcaterrors are:

E/Vold    ( 2280): Failed to find mounted volume for /storage/extSdCard/Android/data/{myApp}/files/
W/Vold    ( 2280): Returning OperationFailed - no handler for errno 0
W/ContextImpl(13364): Failed to ensure directory: /storage/extSdCard/Android/data/{myApp}/files
E/Vold    ( 2280): Failed to find mounted volume for /storage/extSdCard/Android/data/{myApp}/files/
W/Vold    ( 2280): Returning OperationFailed - no handler for errno 0
W/ContextImpl(13364): Failed to ensure directory: /storage/extSdCard/Android/data/{myApp}/files
E/Vold    ( 2280): Failed to find mounted volume for /storage/extSdCard/Android/data/{myApp}/cache/
W/Vold    ( 2280): Returning OperationFailed - no handler for errno 0
W/ContextImpl(13364): Failed to ensure directory: /storage/extSdCard/Android/data/{myApp}/cache

The strange fact is that /storage/extSdCard (symbolic link for /mnt/extSdCard) is not mounted while the external SSD is mounted on /mnt/sdcard

Please help: I'm headbanging.
The first snippet was working as a charm in another project. Could it be the version of ngCordova?


Solution

  • SOLVED:

    after several attempts I solved in this way

    in config.xml:

    <preference name="AndroidPersistentFileLocation" value="Compatibility" />
    <preference name="AndroidExtraFilesystems" value="files,cache, sdcard, cache-external, files-external" />
    

    and main function:

    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {
    
        //var absPath = "file:///storage/emulated/0/";
        var absPath = cordova.file.externalRootDirectory;
        var fileDir = cordova.file.externalDataDirectory.replace(cordova.file.externalRootDirectory, '');
        var fileName = "somename.txt";
        var filePath = fileDir + fileName;
    
        fs.root.getFile(filePath, { create: true, exclusive: false }, function (fileEntry) {
            writeFile(fileEntry, BINARY_ARR).then(function(){
              //do something here
            });
        }, function(err) {});
    }, function(err) {});
    
    function writeFile(fileEntry, dataObj) {
        return $q(function (resolve, reject) {
            fileEntry.createWriter(function (fileWriter) {
                fileWriter.onwriteend = function () {
                    resolve();
                };
                fileWriter.onerror = function (e) {
                    reject(e);
                };
                fileWriter.write(dataObj);
            });
        });
    }

    It seemed that:

    <preference name="AndroidPersistentFileLocation" value="Internal" />
    

    that is the default configuration, could not allow the app to write into the external disk (whether physical or emulated). Instead allowed the app only to write into /data/data/{myApp}/files