You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@cordova.apache.org by "rolinger (via GitHub)" <gi...@apache.org> on 2023/02/27 19:33:01 UTC

[GitHub] [cordova-plugin-file] rolinger opened a new issue, #560: Cordova 11 Android throwing FileError Code 12 trying to access fileSystem

rolinger opened a new issue, #560:
URL: https://github.com/apache/cordova-plugin-file/issues/560

   # Bug Report
   Trying to write to localStorage is breaking on Android with Cordova 11.1.0 (cordova-android 11)
   
   ## Problem
   I am using plugin `cordova-plugin-file-downloader` which is essentially a wrapper for other various plugins.  My app code worked on Cordova 10 but is now breaking on Cordova 11.  I tracked everything down to a `FileError Code 12` which implies the system/folder I am trying to write to doesn't exist, can't be created or doesn't have permission, and the error is being generated from this `cordova-plugin-file` plugin.
   
   The `cordova-plugin-file-downloader` call is simply:
   ```
   downloader.init({folder: 'CustomFolderName', fileSystem: 'file:///storage/emulated/0/'}) ;
      or the default fileSystem:
   downloader.init({folder: 'CustomFolderName''}) ;
   ```
   
   It fails to initialize and immediately throws the FileError code 12.  In <= Cordova 10, this same code worked and on Android devices I could navigate to the devices general Documents folder, open the designated `CustomFolderName` folder and view the file.  I looked at Cordova 10 cordova-plugin-file fileSystem specs and they look the same as cordova 11 fileSystem specs; this I think this is a bug.  Or if it has changed, what should I be using now because I don't am not seeing any differences?
   
   ### What is expected to happen?
   Folder created and document written to that folder
   
   
   ### What does actually happen?
   FileError Code 12 is generated.
   
   
   ## Information
   
   
   ### Command or Code
   <!-- What command or code is needed to reproduce the problem? -->
   
   
   ### Environment, Platform, Device
   Windows 10, Android - all devices
   
   
   ### Version information
   Cordova@11.1
   cordova-android@11
   cordova-plugin-file@7.0.0
   
   
   ## Checklist
   - [x ] I searched for existing GitHub issues
   - [ x] I updated all Cordova tooling to most recent version
   - [x ] I included all the necessary information above
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org
For additional commands, e-mail: issues-help@cordova.apache.org


[GitHub] [cordova-plugin-file] breautek commented on issue #560: Cordova 11 Android throwing FileError Code 12 trying to access fileSystem

Posted by "breautek (via GitHub)" <gi...@apache.org>.
breautek commented on issue #560:
URL: https://github.com/apache/cordova-plugin-file/issues/560#issuecomment-1454808981

   > I presume the LocalFileSystem.PERSISTENT can be replaced with cordova.file.externalDataDirectory or similar? Or is PERSISTENT the only way to make this work?
   
   Not quite. First I want to make clear of two filesystem concepts that Android has:
   
   1. `Internal` storage. This is a embedded chip that is tied to the device itself.
   2. `External` storage. This is an independent storage medium that may or may not exist on the device, or it may change at any time. Most android devices will emulate external storage (in which case it will have a `/storage/emulated/` path), but if the user inserts an SD card, then the external storage path may change to `/sdcard`.
   
   Now `Internal` storage is protected but every application has it's own internal storage sandbox that it basically as free reign over for the most part. Some directories or files may be read-only, such as the `cordova.file.applicationStorageDirectory`, which is the installation directory of your app, which you don't have permission to update, however you can create folders in this path.
   
   `LocalFileSystem.PERSISTENT` is part of the (now defunct) W3C FileSystem API, which the file plugin implements. This is an implementation detail, but currently this uses the `Internal` storage directory. It's the equivalent to `cordova.file.dataDirectory + "files/"`. If you were to use the File Explorer tool, you'll see the following folder path: `/data/data/<app_id>/files/files/`. 
   
   For Android, all of the `cordova.file` directory APIs will refer to a `Internal` storage path, while all `cordova.file.external*` constants will refer to an `External` storage path.
   
   You're pretty much free to create any directory structure inside internal storage without permissions, within your app_id sandbox.
   
   `External` storage has gone through several changes (and further changes are incoming in API 33). And access depends on several factors. For example, every Android app has an external data directory, `cordova.file.externalDataDirectory`, which like Internal storage, you could read and write to without any permissions.
   
   Inside `cordova.file.externalRootDirectory` you'll see the several directories for different media as well as `Downloads` directory, and a few others, which I'm going to refer to these as "External Media" directories.
   
   Android API 28 and earlier, your app could do a lot of different things, everywheres on external storage, including messing around with other app's external storage, as long as you had the `READ/WRITE_EXTERNAL_STORAGE` permission.
   
   Android API 29 have made changes to make external storage slightly more secure, which including locking down some directories. `WRITE_EXTERNAL_STORAGE` no longer does anything, and you may write to External Media directories without any special permissions however you may not overwrite files that already exists if it's owned by a different application. Reading files still requires the `READ_EXTERNAL_STORAGE` permission. On API 29, developers could make use of a `requestLegacyExternalStorage` flag to revert the behaviour back to API 28 behaviour, but this is a request which the OS may reject. I believe it only honours it for users that already had your app installed, so in otherwords, fresh app installs will not honour this request. Lastly, **API 29 is specifically broken 
    with the file plugin because Android does not implement a filesystem API for external storage**. This means if the app wants to access the external file storage, it must use the native MediaStorage APIs, which the file plugin does not and cannot really implement.
    
   Starting with API 30, Android **does** implement filesystem API for external storage which allows for third-party libraries and native code to access the external storage again, which also "fixes" this plugin so to speak. The scoped storage changes that came in API 29 however still applies.
   
   Starting with API 33 (currently not supported), `READ_EXTERNAL_STORAGE` will no longer work and instead they have `READ_MEDIA_*` permissions for images, audio, and video.
    
    > I can write to cordova.file.externalRootDirectory + "MyNewFolder" and download my file there. Ok, so I got all that working again.
   
   Android doesn't make clear but they have said that some directories will be inaccessible. Therefore I wouldn't rely on using custom directories in external root, and instead either use your app's internal or external directory. Therefore I'd consider preparing a migration strategy if necessary.
   
   > However for iOS, I am continually perplexed. For iOS I have now used both cordova.file.externalDataDirectory and cordova.file.dataDirectory.....
   
   I'm significantly less knowledgable with iOS, and I'm not sure how `externalDataDirectory` behaves for iOS (it's not documented...) but I do know that iOS does not have an internal/external storage concept like Android. iOS storage model is far more simple. Your app has an filesystem sandbox that is completely private. It's comparable to Android's internal storage concept. Sharing files between apps happens via non-filesystem APIs I believe, there is no way so share files between apps using purely the filesystem. So in otherwords, a browser app downloading a file may write to it's app storage sandbox, but use a non-filesystem API to share it in a more public place... For example, in order for the iOS to move a file to a shared location, it must use something like [UIDocumentPickerViewController](https://developer.apple.com/documentation/uikit/uidocumentpickerviewcontroller?language=objc) which brings up a save dialog and the user chooses where to put the file. The iOS app itself d
 oes not have direct access to anything outside of it's sandbox. I don't believe anything like this is implemented in the file plugin, as it's aimed to be... a filesystem API.
   
   Hope this knowledge helps somehow.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org
For additional commands, e-mail: issues-help@cordova.apache.org


[GitHub] [cordova-plugin-file] breautek commented on issue #560: Cordova 11 Android throwing FileError Code 12 trying to access fileSystem

Posted by "breautek (via GitHub)" <gi...@apache.org>.
breautek commented on issue #560:
URL: https://github.com/apache/cordova-plugin-file/issues/560#issuecomment-1446968572

   Android's scoped storage forbids creating custom directories in external storage root and some other directories. Scoped storage is enforced on all API 29 devices and later.
   
   Additionally, API 29 SDK lacks the FileSystem API for scoped storage, which effectively breaks this plugin as well as any library that depends on using the Java's File APIs when trying to access the external storage.
   
   So instead of using `'file:///storage/emulated/0/'`, (which may not even exist on all android devices, you should use `cordova.file.externalRootDirectory` to refer to this directory), try using your application's external storage directory instead, `cordova.file.externalDataDirectory`.
   
   If using the external storage is not a requirement, it may be better to use the internal storage instead (and avoid all the external storage limitations, including the API 29 problem explained above): `cordova.file.dataDirectory`.
   
   Let me know if this helps.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org
For additional commands, e-mail: issues-help@cordova.apache.org


[GitHub] [cordova-plugin-file] rolinger commented on issue #560: Cordova 11 Android throwing FileError Code 12 trying to access fileSystem

Posted by "rolinger (via GitHub)" <gi...@apache.org>.
rolinger commented on issue #560:
URL: https://github.com/apache/cordova-plugin-file/issues/560#issuecomment-1454779591

   @breautek - a follow up on this.  Could use your input.
   
   For Android, I cannot write directly to: `cordova.file.externalRootDirectory` anymore.  I guess it changed in API 29/30.  However, targeting API 32, I can write to `cordova.file.externalRootDirectory + "MyNewFolder"` and download my file there.  Ok, so I got all that working again.
   
   However for iOS, I am continually perplexed.  For iOS I have now used both `cordova.file.externalDataDirectory` and `cordova.file.dataDirectory` - in BOTH cases, my app can read the root directories and create the `MyNewFolder` in both and then write files to that new folder.  In subsequent passes, my app can then read the contents of `MyNewFolder` (in both locations) and see the downloaded file there....ALL via console.log messages.
   
   However, on the device itself, the `MyNewFolder` and its file are not accessible in any place that I can find.  The folder and new file now exists, but how does the user access them?  In the device app `Files`, then in the `On my iPhone` option, it takes me to an empty folder.  But if I manually download a file from Safari, a new folder `Downloads` appears  in the `On My iPhone` section and the manually downloaded file is now in that folder.  Safari must be creating that `Downloads` folder on the first manual download.
   
   In my app, I then try to find this new `Downloads` folder (printing the contents of every `cordova.file.PATH` to read its contents (and maybe use it as the future directory to create `MyNewFolder` and write my files there) but I can't find the `Downloads` folder anywhere.
   
   Please help me resolve this.  I have spent days on this and am pulling out what little hair I have left.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org
For additional commands, e-mail: issues-help@cordova.apache.org


[GitHub] [cordova-plugin-file] rolinger commented on issue #560: Cordova 11 Android throwing FileError Code 12 trying to access fileSystem

Posted by "rolinger (via GitHub)" <gi...@apache.org>.
rolinger commented on issue #560:
URL: https://github.com/apache/cordova-plugin-file/issues/560#issuecomment-1447382083

   @breautek - thanks for quick response.  
   
   On both Simulator (Pixel 3a Android 10) and on real Samsung Flip 4 (Android 13) the `cordova.file.externalRootDirectory` resolves as `file:///storage/emulated/0/`  causing the FileError Code 12 on both.
   
   On both, `externalDataDirectory` resolves as: `file:///storage/emulated/0/Android/data/com.myApp/files/`
   
   And changing it from `externalRootDirectory` to `externalDataDirectory` got past the issue, still working on other issues related now - just concerned the final saved document is not going to be in a place where the users can access them.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org
For additional commands, e-mail: issues-help@cordova.apache.org


[GitHub] [cordova-plugin-file] rolinger commented on issue #560: Cordova 11 Android throwing FileError Code 12 trying to access fileSystem

Posted by "rolinger (via GitHub)" <gi...@apache.org>.
rolinger commented on issue #560:
URL: https://github.com/apache/cordova-plugin-file/issues/560#issuecomment-1457359007

   @breautek - well after a VERY long time of frustration, I finally found the answer for iOS.
   
   Please update the `cordova-plugin-file` documentation.  Honestly, I can't believe its not listed anywhere in official Cordova documentation; especially the `cordova-plugin-file` docs.  For anyone wanting to use `cordova-plugin-file` (and maybe `cordova-plugin-file-transfer`) to save files to the public `On My iPhone --> Downloads` folder, they will need to add the following TWO keys to their apps `info.plist` file:
   
   ```
       <key>UIFileSharingEnabled</key>
       <true/>
       <key>LSSupportsOpeningDocumentsInPlace</key>
       <true/>
   ```
   
   Yup...THATS IT.  What this does is create symlink to everything in the apps documents folder (ie: `cordova.file.documentsDirectory`).   When the user goes to the `On My iPhone` root folder, they will see a `Downloads` folder AND individual app folders for every app that has these values set to `true`.  This allows users to find/navigate every apps root Documents folder as if it were a part of their general `Downloads` folder.
   
   Unreal....spun my wheels on this one...well....for at least 3 years.  Always coming up with some ugly hybrid that was always break; esp with each new version of iOS.  But again....in all the years of searching for a solution, I never once saw any reference to these keys before.  Unreal its not included in any Apache Cordova docs.   
   
   Actually, there is an OPEN request to document this very thing....its three years old!  https://github.com/apache/cordova-plugin-file/issues/392 - in fact, if you do a google search for `cordova UIFileSharingEnabled` VERY little comes back.  How come, in general, the community simply does not know about these keys? 
   
   Sorry, on a bit of rant....mind blowing how this has escaped so many for so long.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org
For additional commands, e-mail: issues-help@cordova.apache.org