Error when I try to read a file using Capacitor in Android 11

I have an app using Ionic and Capacitor with target API 29, but I need to change to API 30 because of PlayStore rules.

My problem with this transition is with capacitor filesystem, which is not finding files in my device. I need to read these files to import in the map in my app.

I tried to read files and directories, tried to create some random files and use stat to get files information, but sometimes I can read these files and sometimes I don’t. I tried to find a pattern in this errors with some tests, but I couldn’t find or understand the pattern.

Initially I thought that this problem was caused by external storage permissions, so I added in the manifest the relevant parameters, even so the errors persisted in my app. I started to think that this can be related to Android Unix Permissions, but I can’t say if that is true. I tried to use checkPermissions() of Capacitor, but I received the ‘granted’ response.

External Storage parameters in AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

I tried to research for permission problems related to capacitor filesystem, but the only question I found is my own about the same subject, created a while ago. This question have an answer, but it is but not applicable for API 30.
I also tried to research about filesystem android permissions too, I found one answer about this subject, but this only tell me that I can’t modify the FS permission properties.

Some things I tried to do:

First I tried to use readFile() in a file on external storage:

this.fs.readFile('Download/test.kml', true).then(response => {
  console.log(response)
});

readFile() function inside FS service:

/** Read a file and returns a string as base 64 */
readFile(path: string, needDirectory: boolean = false, dir: string = 'ExternalStorage'): Promise<string> {
	let options: ReadFileOptions;
	if (needDirectory) {
		options = {
			path: path,
			directory: Directory[dir]
		}
	} else {
		options = {
			path: path
		}
	}

	return Filesystem.readFile(options).then(result => {
		return result.data;
	});
}

But it didn’t work, then I tried to create some files using writeFile(), in tree different directories:

this.fs.writeFile('Download/', 'test.kml', "<xml version='1.0' encoding='utf-8'></xml>", true, undefined, true);
this.fs.writeFile('/', 'test.kml', "<xml version='1.0' encoding='utf-8'></xml>", true, 'External', true);
this.fs.writeFile('/', 'test.kml', "<xml version='1.0' encoding='utf-8'></xml>", true, 'Documents', true);

writeFile() function inside FS service:

/** Write a file, content in base64, Pass encoding to true to write data as string */
writeFile(path: string, filename: string, content: any, recursive: boolean = false, dir: string = 'ExternalStorage', encoding: boolean = false): Promise<boolean> {
	let options: WriteFileOptions = {
		path: path + filename,
		data: content,
		directory: Directory[dir],
		recursive: recursive
	}
	if (encoding) {
		options.encoding = Encoding.UTF8
	}
	
	return Filesystem.writeFile(options).then(result => {
		if (result) {
			return true;

		} else {
			return false;
		}
	})
}

This worked! Then I tried to read all these newly created files:

this.fs.readFile('Download/test.kml', true);
this.fs.readFile('test.kml', true, 'External');
this.fs.readFile('test.kml', true, 'Documents');

This worked too!, Now, in my head the problem is with the files that aren’t created by my app, but the files that already exist in FS or are moved by the user. So I tried to use readDir to test what the app sees:

this.fs.readDir('/', undefined, true)
this.fs.readDir('/', 'External', true)
this.fs.readDir('/', 'Documents', true)

readDir function into de fs service:

/** Read dir and list this files */
readDir(path: string, dir: string = 'ExternalStorage', needDirectory: boolean = true): Promise<ReaddirResult> {
	let options: ReaddirOptions;
	if (needDirectory) {
		options = {
			path: path,
			directory: Directory[dir]
		}
	} else {
		options = {
			path: path
		}
	}
	return Filesystem.readdir(options).then(files => {
		return files;
	}).catch(e => {
		console.log(e);
		let resultError: ReaddirResult = {
			files: []
		};
		return resultError;
	})
}

The readDir() returns me only the folders that my app created, but there are others files in this directory, those that were created manually didn’t appear. Then I tried to use the stat() function in one of this files that my app didn’t saw:

this.fs.getStat('USO.kml', 'Documents')
this.fs.getStat('USO.kml')

getStat() function inside FS service:

/** 
 * Get file information's: type, size, ctime(Create time), mtime(Last modification), uri
 * @param path path of file
 * @param dir directory where is file
*/
getStat(path: string, dir: string = 'ExternalStorage'): Promise<StatResult> {
	return Filesystem.stat({
		path: path,
		directory: Directory[dir]
	}).then(result => {
		return result;
	});
}

I can’t understand, because the file USO.kml in Documents directory appear in stat function, but another one in external storage returns me an error File does not exist. I don’t understand why it’s happening. How can I correctly read the files using Capacitor FS and API 30?

Edit

Just to clarify: The file is moved externally by the user and saved inside this directory through Android explorer. I need to import this file to my app to get the information inside.

1 Like

Hi there… did you manage to solve the issue? I having same weird issue on reading file on Mobile

If you’re targeting Android 30 or 31, you’ll need to add requestLegacyExternalStorage=true to your AndroidManifest. That is a common source of problems in Android apps. For more info on that check out this google page on filesystem changes on new Android versions