How to write data to a file using ngCordova?

I am using ngCordova extension for AngularJS. Till now I have found it a very powerful extension, which helps you create AngularJS + Cordova applications in a go. I wanted to use File read/write functionality in one of my app. I have successfully implemented the File Reading functionality using ngCordova. Now, I want to know the way to write the data to a file. I would be thankful if I get any demo code for the same.

1 Like

You should be able to with ngCordova,

  $cordovaFile.writeFile(filePath).then(function(result) {
      // Success! 
  }, function(err) {
      // An error occured. Show a message to the user
  });

I’m also looking for an answer to the question asked.

I don’t understand where to put the data that you actually want to write.

As far as I can see, the code above writes a blank file.

How would I for example write some JSON to a file?

I’m looking for a real example of writing data as well. same as steviewevie. hopefully one that suggests a standard file path as well

Had the same problem, searched the ng-cordova.js file to see what was up.
The docs are missing two arguments for the writeFile function.

Try this, it worked for me:

$cordovaFile.writeFile( 'file.txt', data, {'append':false} ).then( function(result) {
        // Success!
}, function(err) {
	// An error occured. Show a message to the user
});

1 Like

I’m having problems writing a dataURL to a file.jpg on iOS. I keep getting error {"code":5}

I know I should convert the dataURL to a Blob, but that shouldn’t keep me from writing a file.

Also, if I just provide a filename (no path) the save is successful, but a checkFile still gives me the same error {"code":5}

any ideas?

// INFO: Local cache folder opened: file:///var/mobile/Containers/Data/Application/[UUID]/tmp/imgcache/

//DOESN'T WORK AT ALL
//filePath=file:///var/mobile/Containers/Data/Application/[UUID]/tmp/imgcache/12345.jpg
//dataURL.slice(0,50)=data:image/jpg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4
return $cordovaFile.writeFile(filePath, dataURL, {
                'append': false
              }).then(function(result) {
                console.log("$ngCordovaFile writeFile SUCCESS");
                console.log(result);
                return $q.when(result);
              }, function(error) {
                console.log("$ngCordovaFile writeFile ERROR");
// $ngCordovaFile writeFile ERROR
                console.log(error);
// {"code":5}
                return $q.reject(error);
              });
//works for filePath=12345.jpg, cdvfile://localhost/persistent/12345.jpg
//dataURL.slice(0,50)=data:image/jpg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4
return $cordovaFile.writeFile(filePath, someBlob, {
                'append': false
              }).then(function(result) {
                console.log("$ngCordovaFile writeFile SUCCESS");
                console.log(result);
//{"type":"writeend","bubbles":false,"cancelBubble":false,"cancelable":false,"lengthComputable":false,"loaded":0,"total":0,"target":{"fileName":"","length":344286,"localURL":"cdvfile://localhost/persistent/12345.jpg","position":344286,"readyState":2,"result":null,"error":null,"onwritestart":null,"onprogress":null,"onwrite":null,"onabort":null,"onerror":null}}
                return $q.when(result);
              }, function(error) {
                console.log("$ngCordovaFile writeFile ERROR");
                console.log(error);
                return $q.reject(error);
              }).then(function(result) {
                filePath = result['target']['localURL'];
                console.log("\n\n >>> $ngCordovaFile.checkFile() for file=" + filePath);
// >>> $ngCordovaFile.checkFile() for file=cdvfile://localhost/persistent/12345.jpg
                return $cordovaFile.checkFile(filePath);
              }).then(function(result) {
                console.log(result);
                console.log("$ngCordovaFile says file EXISTS file=" + filePath + "\n\n");
                return result;
              })["catch"](function(error) {
                console.log(error);
//{"code":5}
                console.log("$ngCordovaFile.checkFile() says DOES NOT EXIST, file=" + filePath + "\n\n");
//$ngCordovaFile.checkFile() says DOES NOT EXIST, file=cdvfile://localhost/persistent/12345.jpg
                return $q.reject(error);
              });

I hope this will help you out:

app.controller(‘TestCtrl’, [’$scope’, ‘$q’, ‘$cordovaFile’,
function($scope, $q, $cordovaFile) {

console.log('TestCtrl');

/*
 List dir test and remove all dirs and files in test to start over again the test
*/
    function ClearDirectory() {
console.log('ClearDirectory');
		$cordovaFile.listDir(fileDir + 'test').then( function(entries) {
console.log('listDir: ', entries);
		}, function(err) {
console.error('listDir error: ', err);
		});

		$cordovaFile.removeRecursively(fileDir + 'test')
		.then( function() {
console.log(trinlDir + ' recursively removed');
		},
		function(err) {
console.log(fileDir + ' error: ', err);
		});
	}

/*
Here some examples with proper filepath
*/

	function testFS() {
	// Download file from 'http://www.yourdomain.com/test.jpg' to test/one/test.jpg on device Filesystem
		var hostPath = 'http://www.yourdomain.com/test.jpg';
		var clientPath = fileTransferDir + 'test/one/test.jpg';
		var fileTransferOptions = {};

		$cordovaFile.downloadFile(hostPath, clientPath, true, fileTransferOptions).then (function() {
		});
	// Create dir test
		$cordovaFile.createDir(fileDir + 'test/').then( function(dirEntry) {
		});
	// Create dir aganin in dir test
		$cordovaFile.createDir(fileDir + 'test/one/').then( function(dirEntry) {
		});
	// Create empty file test.txt in test/again/
		$cordovaFile.createFile(fileDir + 'test/one/test.txt', true).then( function(fileEntry) {
		});
	// List of files in test/again
		$cordovaFile.listDir(fileDir + 'test/one/').then( function(entries) {
		console.log('list dir: ', entries);
		});
	// Write some text into file 
		$cordovaFile.writeFile(fileDir + 'test/one/test.txt', 'Some text te test filewrite', '').then( function(result) {
		});
	// Read text written in file
		$cordovaFile.readAsText(fileDir + 'test/one/test.txt').then( function(result) {
	console.log('readAsText: ', result);
		});
	}

    function testQ() {
		var hostPath = 'http://www.yourdomain.com/test.jpg';
		var clientPath = fileTransferDir + 'test/one/test.jpg';
            var fileTransferOptions = {};
		$q.all([
			$cordovaFile.downloadFile(hostPath, clientPath, true, fileTransferOptions),
			$cordovaFile.createDir(fileDir + 'test/'),
			$cordovaFile.createDir(fileDir + 'test/two/'),
			$cordovaFile.createFile(fileDir + 'test/one/test.txt', true),
			$cordovaFile.listDir(fileDir + 'test/one/'),
			$cordovaFile.writeFile(fileDir + 'test/one/test.txt', 'Some text te test filewrite', ''),
			$cordovaFile.readAsText(fileDir + 'test/one/test.txt')
		]).then( function(result) {
console.log('testQ result: ', result);
		});
    }

/*
Here is what I am using for my Android and IOS apps
Keep attention to a couple of things:
-	Android and IOS have other directorynames for files
-	$cordovaFile functions prefixes all pathnames with root
	$cordovaFileTransfer functions needs absolute pathnames

Here I create the prefixes for File functions and FileTransfer functions for Android and IOS
*/

    $ionicPlatform.ready(function() {
		if (ionic.Platform.isAndroid()) {
    console.log('cordova.file.externalDataDirectory: ' + cordova.file.externalDataDirectory);
    			myFsRootDirectory1 = 'file:///storage/emulated/0/'; // path for tablet
    			myFsRootDirectory2 = 'file:///storage/sdcard0/'; // path for phone
    			fileTransferDir = cordova.file.externalDataDirectory;
    			if (fileTransferDir.indexOf(myFsRootDirectory1) === 0) {
    				fileDir = fileTransferDir.replace(myFsRootDirectory1, '');
    			}
    			if (fileTransferDir.indexOf(myFsRootDirectory2) === 0) {
    				fileDir = fileTransferDir.replace(myFsRootDirectory2, '');
    			}
    console.log('Android FILETRANSFERDIR: ' + fileTransferDir);
    console.log('Android FILEDIR: ' + fileDir);
    		}
    		if (ionic.Platform.isIOS()) {
    console.log('cordova.file.documentsDirectory: ' + cordova.file.documentsDirectory);
    			fileTransferDir = cordova.file.documentsDirectory;
    			fileDir = '';
    console.log('IOS FILETRANSFERDIR: ' + fileTransferDir);
    console.log('IOS FILEDIR: ' + fileDir);
    		}

		if (ionic.Platform.isAndroid() || ionic.Platform.isIOS()) {
			ClearDirectory();
			testFS();
// Other functions here
		}
	});

}]);
5 Likes

thank pcr:
$cordovaFile.writeFile don’t accept cordova.file.externalDataDirectory, you must remove root dir.

	fileDir = cordova.file.externalDataDirectory.replace(cordova.file.externalRootDirectory, '');
var filePath = fileDir+"allNews.txt";		
    //or filePath = "allNews.txt";	
$cordovaFile.writeFile(filePath, data, options).then(function(result) {
      //success
 }, function(err) {
 // An error occurred. Show a message to the user
});
1 Like

You are absolutely right. In my code writeFile doesn’t use directly cordova.externalDataDirectory. In my code you see that path in my ngCordova functions use a prefix. Scroll down in my code. At the very end I define prefixes for platform Android or IOS. For Android devices there are some other things to do because, for example: Samsung SII phone uses another root than Samsung Tab 3. Maybe there are some other devices with specific rootdir. If you read my code at the end you should what to to if a device uses another root. Simply add a rootDir that should be removed for using ngCordova functions.These prefixes I use in all my ngCordova functions. I tested this approach very often.

thank pcr, i use your code in my project.

@pcr Between this code you posted and my own experiments, it seems to me that the ONLY directory cordovaFile can access on android is the externalDataDirectory…?

When I run $cordovaFile.listDir(’’), I only get paths associated with the externalDataDirectory (storage/sdcard0 on my test phone, as you noted), and I cannot seem to figure out how to point $cordovaFile at other file systems, even though, for example, I can resolve cordova.file.cacheDirectory via window.resolveLocalFileSystemURL, it definitely exists, and has a native url of file:///data/data//cache

Am I missing something?

@nandanrao In my case it is like the:
I use Samnsung SII and Samsung Tab 3. I wanted to use the storage on external SdCard. But for some reason these devices cordova dont detect these sdcards. As a foldback Samsung devices redirct to the internal SdCard. So my code works. I wait for external untill cordova or Samsung has solved this issue.
If you have another device and this dont work you replace externalDataDirectory with dataDirectory. Now you are using directly the internal Storage.
I never tried the cache directory. I’ll try asap and let you know…

You are right. cordova.file.cacheDirectory doesn’t work with my solution. I am not sure if it is my solution. I’ll investigate why?

Did you guys resolve this? I am also trying to write a dataURI to a file but keep getting the encoding error…

This was really great thank you @pcr.

We came across some odd situations which meant we couldn’t use these values:

So we tweaked @pcr’s solution a little. We’re getting the file system ‘root’ DirectoryEntry and using that to generate the relative file path:

var absolutePath = '';
var relativePath = '';

window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fileSys) {
   
            fileSys.root.getDirectory('', { create: true, exclusive: false }, function (dir) {

                if (ionic.Platform.isAndroid()) {
                    var rootDirectory = dir.toURL();
                    absolutePath = cordova.file.externalDataDirectory;
                    if (absolutePath.indexOf(rootDirectory) === 0) {
                        relativePath = absolutePath.replace(rootDirectory, '');
                    }                        
                } else if (ionic.Platform.isIOS()) {
                    // same as @pcr above
                }

            }, onFail);
        }, onFail);

Thank you @benwood. I hope this workshop for all devices. Then this is the simple way to get the relative path.

@pcr Thanks for this workshop! I’ve followed your suggestions on how to use the file plugin on this thread and all your other posts in various threads regarding ngcordova plugins. I am still getting TypeError: Cannot read property ‘applicationDirectory’ of undefined.

I am developing for the Samsung Tab 4.
My system info:
Cordova CLI: 5.0.0, Ionic Version: 1.0.0-beta.14, Ionic App Lib Version: 0.0.19, OS: Mac OS X Yosemite, Node Version: v0.12.0.
I’ve followed your suggestions on when a plugin is undefined:

  1. I removed the plugin
  2. I removed the platform
  3. I added the platform (android 4.0.0)
  4. I added the plugin through cordova plugin add cordova-plugin-file in Terminal
  5. I rebuild my platform
  6. I updated it

I’ve been googling, stackoverflowing, and forum.ionicframework’ing for two weeks now, trying to fix this bug.

PLEASE HELP!

Thanks in advance,
William

This is the best approach - root dir can be different on different devices, best to calculate at run-time instead of hard-coding the prefix.

Maybe you can show us the fragment of code the error message refers to?

“Undefined” typically means the object is not available (plugin not installed, not injected, etc etc). You can check if the native part is installed under platforms/android/src. The JS part needs to be in platforms/android/assets/www. Also make sure the relevant ngCordova service is being injected into your code.

@willy_wonga
I have some questions to help you:
You say an error like ‘applicationDirectory’ of undefined. The undefined object as far as I know is cordova. This means there is something not loaded or you don’t wait for device ready. Are you sure ngCordova is loaded (with a script tag in your index.html). Look in Chrome devtools if the has loaded this file.
For me the most made misstake is not waiting for deviceready.