apache / cordova-plugin-file

Apache Cordova File Plugin
https://cordova.apache.org/
Apache License 2.0
740 stars 757 forks source link

Android receiving Code 2 when trying to create folder in externalRootDirectory #604

Closed rolinger closed 8 months ago

rolinger commented 8 months ago

Well, this issue is back ...and its beyond frustrating. My app is compileSDK/targetSDK Android API 33.

My app allows users to download files to public folders created by my app; my app doesn't need access to other files in other folders created by other apps. I managed to have everything working

You can't directly write to: file:///storage/emulated/0/Download You can create and write to: file:///storage/emulated/0/Download/SomeFolder

In my case, for organizational purposes, my app is creating and writing to the following folders:

file:///storage/emulated/0/Download/MyAppName/sub-folder1/thisFile1.doc
file:///storage/emulated/0/Download/MyAppName/sub-folder2/thisFile2.doc
...etc, etc

For the record, most or all of the files my app is downloading are office type files (IE: doc,xls,pdf, etc).

This above was all working. Then I deleted app and re-installed and began further testing other components and when I came back to file downloads nothing would work.

var dir = "file:///storage/emulated/0/Download/" ;
var newDir = "MyAppName" ;
window.resolveLocalFileSystemURL(dir,function(dirEntry) {
   dirEntry.getDirectory(newDir, {create: true, exclusive: false},
     function(success) {
         console.log("Directory Exists") ;
     },
     function(err) {
         console.log("Failed") ;
         console.log(err) ;
     }) ;
}) ;

I keep getting Code: 2 - which is a security code, implying my app cannot see and/or create directories created by my app before the delete. That is somewhat expected as Android probably sees the new install as a different app than the one that create the original directories. But when I change the root dir to MyAppName1 - a directory that has never existed in ../Download/ i get the same Code: 2 error.

Additionally, I have found:

  1. If the user, using a File Manager app, manually deletes the file OR the sub-folder OR the root MyAppName folder - that the entire path is now unavailable to my app. My app cannot recreate any of the root or sub-folders or that file name again. If the path folders still exist, and the user just deletes the file itself, I can download the same file but have to use a new saved file name, it won't let me save the same file name if it had once previously existed.. But if a part of the path or the full path is unavailable I am forced to create a new directory tree ../Download/MyAppName/sub-folder11/thisFile11.doc OR ../Download/MyAppName1/sub-folder1/thisFile1.doc

The problem in both cases is that if I have to if have to check for the root MyAppName folder, but if it was manually deleted or the app was reinstalled, I now have to create MyAppName1 - but since there is no way to READ the existence of MyAppName, I have to just keep looping through until I can create a new one. If MyAppName1 was used before, then I have to try MyAppName2 and so forth.

Both are quite nasty, I understand reinstalling an app it treats the original folder path as being owned by a different app instance or altogether created by a different app (something they should be able to fix), but #1 is particularly nasty because it is somehow caching the file returning an error code containing "EEXISTS" - when clearly the user has deleted the file and it doesn't exist - and then download the file again using a different file name. I think this one is a bug.

breautek commented 8 months ago

You're probably better off migrating to a SAF/MediaStore plugin to manage files in external storage. See this for more details. File plugin can still be used for internal storage without limitations, but using file plugin for external storage with Android's scoped storage mechanisms will cause a lot of headaches.

While the deletion scenario you described does seem to be buggy behaviour, it's not something that Cordova can act on unfortunately. That mechanism is likely controlled by Android's libfuse system, which bridges MediaStorage/scoped storage rules into the kernel.

Even if you do manage to workaround this issue, Android's FUSE system was only implemented in Android 11 (API 30), but scoped storage was introduced in Android 10 (API 29). This means that specifically on API 29 devices, accessing external storage via direct file APIs will not work and the only solution to this is to use a plugin that interfaces MediaStore API.