apache / cordova-plugin-file

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

filewriter always throws error code 2 #577

Closed NAMUS09 closed 7 months ago

NAMUS09 commented 10 months ago

Bug Report

when writing file in this path " window.cordova.file.externalRootDirectory + 'Download' " always throws error in filewriter.onerror. Issue not there in cordova v11, cordova-android v11 and cordova-plugin-file v7.

Problem

What is expected to happen?

It should write the file and return success.

What does actually happen?

throws error code 2

Information

tying to save pdf in download folder. I tried updating the plugin file to v8. Still issue is there.

Command or Code

private writeToFile(pdfData: BlobPart, fileName: string): Promise { return new Promise((resolve, reject) => { var storageLocation = '';

  switch (window.cordova.platformId) {
    case 'android':
      storageLocation = window.cordova.file.externalRootDirectory + 'Download';
      break;

    case 'ios':
      storageLocation = window.cordova.file.documentsDirectory;
      break;
  }

  var folderPath = storageLocation;
  console.log(folderPath);

  window.resolveLocalFileSystemURL(
    folderPath,
    (directoryEntry: any) => {
      directoryEntry.getFile(
        fileName,
        { create: true },
        (fileEntry: any) => {
          fileEntry.createWriter(
            (fileWriter: any) => {
              fileWriter.onwriteend = () => {
                resolve('success');
                console.log('File written successfully.');
              };

              fileWriter.onerror = (error: any) => {
                console.log('Error writing file: ');
                reject(error);
              };

              const dataObj = new Blob([pdfData], { type: 'application/pdf' });
              console.log(dataObj);

              fileWriter.write(dataObj);
            },
            () => {
              reject('Error creating file writer.');
              console.log('Error creating file writer.');
            }
          );
        },
        () => {
          reject('Error getting file entry.');
          console.log('Error getting file entry.');
        }
      );
    },
    () => {
      reject('Error resolving file system URL.');
      console.log('Error resolving file system URL.');
    }
  );
});

}

Environment, Platform, Device

while targeting to api 33 issue occurs. There is no issue in cordova v11 ,cordova-android v11 and cordova-plugin-file v7 targeting to api 32.

Version information

Cordova CLI v12, Cordova-android v12 Cordova v8, v7 (both tested)

Checklist

EYALIN commented 10 months ago

@NAMUS09 I added a PR that will solve it https://github.com/apache/cordova-plugin-file/pull/581

@breautek can you please merge it?

MauriceFrank commented 10 months ago

file.removeFile(dir,name) throws permission error:

FileError {code: 6, message: 'NO_MODIFICATION_ALLOWED_ERR'} code : 6 message : "NO_MODIFICATION_ALLOWED_ERR"

Is this fixable with the READ_MEDIA_IMAGES permission somehow? I tried your current Master @EYALIN in hope that it fixes removing files from external data directory also.

I Can access files with the READ-permission but it seems like removing and writing files is a problem on Android 13.

Any ideas? I would be glad for any help.

Thank you!

breautek commented 10 months ago

I Can access files with the READ-permission but it seems like removing and writing files is a problem on Android 13.

PR #581 really only fixes the plugin by removing the request attempt on API 33+ for the obsolete WRITE_EXTERNAL_STORAGE permission which on API 33 is an auto rejection.

It assumes true which is not necessarily the case. With the scoped storage system that Android has, you no longer need permission to write to external storage, however cannot modify files owned by other apps, which is not possible at all using any file-system APIs. There is no permission grant that the app itself can request to gain permission to modify other app's files.

Instead, apps is suppose to use the MediaStore API which allows the app to request permission to write to a specific file (assuming that the owning app has a content provider implemented), allowing a temporary write grant to your app owned by a third-party app.

I've wrote a pretty large comment elsewhere that explains the nuances with interfacing the file plugin with the MediaStore API. TL;DR; I don't think it's feasible and if you require write access to third-party files, then you'll probably need a separate plugin that interfaces with the MediaStore API.

EYALIN commented 10 months ago

@breautek thanks for all your help. I see that you are very active in a few plugins. highly appreciate!

MauriceFrank commented 10 months ago

I Can access files with the READ-permission but it seems like removing and writing files is a problem on Android 13.

PR #581 really only fixes the plugin by removing the request attempt on API 33+ for the obsolete WRITE_EXTERNAL_STORAGE permission which on API 33 is an auto rejection.

It assumes true which is not necessarily the case. With the scoped storage system that Android has, you no longer need permission to write to external storage, however cannot modify files owned by other apps, which is not possible at all using any file-system APIs. There is no permission grant that the app itself can request to gain permission to modify other app's files.

Instead, apps is suppose to use the MediaStore API which allows the app to request permission to write to a specific file (assuming that the owning app has a content provider implemented), allowing a temporary write grant to your app owned by a third-party app.

I've wrote a pretty large comment elsewhere that explains the nuances with interfacing the file plugin with the MediaStore API. TL;DR; I don't think it's feasible and if you require write access to third-party files, then you'll probably need a separate plugin that interfaces with the MediaStore API.

Thanks for the detailed clarifications! I was part of this Android 13 conversation here too some weeks ago when I created a PR to fix Read permissions. This solved it at least for reading images in the gallery for me. I saw that it got merged. Still I noticed it is not the full solution and at this time I knew you were all waiting for cordova-android 12 which is finally here! I Just saw today that there is cordova-android 12 and the update on this plugin and im happy that it is still maintained by the community and you.

Unfortunately as I see now, Android is only allowing read-permissions to external storage with the normal file-system as you described in your post. So I really have to try alternative cordova plugisn using the MediaStore. I did not have success in the past with some of them but there might be updates and since the cordova-android 12 release, it might have changed too,

I will give this plugin another try https://github.com/apache/cordova-plugin-file/issues/cordova-plugin-saf-mediastore. At least only for the parts, where I have to remove images from the gallery.

Thank you for all the research!

breautek commented 10 months ago

I still need to do more research on the implications on removing support for external storage when it comes to other plugins like the camera plugin that kinda uses external storage to some fashion (not that the file plugin works well to begin with in this area) but I feel like removing external support on the file plugin is going to end up being the best path forward, which leaves the file plugin purely for internal storage only, which still has a fully functional filesystem API on Android. For use cases involving external storage, a different plugin will work much better, since it can have an API more tailored to the MediaStore interface, like the cordova-plugin-saf-mediastore for example.

Once I understand what kind of problems will arise to dependent Apache plugins if the external storage support is removed, I'll open a topic on the Dev Mailing List for a more formal community vote on the subject, since it will be a fairly large change.

Only thing is... I don't use any external storage stuff at my day job, so I don't have much of an excuse to do allocate work time on this. So this is strictly volunteer time on my part, and well lately I've been pretty burnt out.

Krantikc commented 10 months ago

Even I am facing similar issue. It throws error code 2 while downloading the image/pdf file. It is failing while executing getDirectory command from DirectoryEntry.js (cordova-plugin-file) when targeted android API level to 33. exec(win, fail, 'File', 'getDirectory', [this.toInternalURL(), path, options]).

I have also included required permissions in config.xml

<config-file parent="/manifest" target="app/src/main/AndroidManifest.xml" mode="merge">
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
</config-file>

Any solutions for this is appreciated. Thanks in advance !!

EYALIN commented 10 months ago

@Krantikc it is not enough. use my PR, it will solve your issue https://github.com/apache/cordova-plugin-file/pull/581

Krantikc commented 10 months ago

@Krantikc it is not enough. use my PR, it will solve your issue #581

@EYALIN Thanks for suggestion, I have already tried your forked repo, but still no luck. I am wondering if I have to make any other modifications.

EYALIN commented 10 months ago

@Krantikc try to add logs and to see in log cat. from what i see, you will arrive here:

private boolean hasReadPermission() {
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            return PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_IMAGES) 
            && PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_VIDEO)
            && PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_AUDIO);
          } else {
            return PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
          }
    }

or here:

  private boolean hasWritePermission() {
          if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                return true;
              } else {
                return PermissionHelper.hasPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
              }
    }

please make sure you have those changes (for the write permission).

try remove ing the plugin and add it from my fork, i tried on few devices android 10-13 and all works. cordova plugin remove cordova-plugin-file cordova plugin add https://github.com/EYALIN/cordova-plugin-file.git

Krantikc commented 10 months ago

@Krantikc try to add logs and to see in log cat. from what i see, you will arrive here:

private boolean hasReadPermission() {
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            return PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_IMAGES) 
            && PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_VIDEO)
            && PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_AUDIO);
          } else {
            return PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
          }
    }

or here:

  private boolean hasWritePermission() {
          if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                return true;
              } else {
                return PermissionHelper.hasPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
              }
    }

please make sure you have those changes (for the write permission).

try remove ing the plugin and add it from my fork, i tried on few devices android 10-13 and all works. cordova plugin remove cordova-plugin-file cordova plugin add https://github.com/EYALIN/cordova-plugin-file.git

@EYALIN Thanks a lot for quick reply, it's working fine. My bad, after importing your forked repo, issue was resolved, but file was not opening up because of cordova-plugin-file-opener2 plugin. I was getting NOT_FOUND error, as there was mismatch in file protocol, whose way handling has been changed as part of cordova-plugin-file-opener2@7.0.0.

Thanks Again !! :)

EYALIN commented 10 months ago

@NAMUS09 @Krantikc no problem :-) regarding the file opener, so you can use my plugin which I maintain and update all the time. https://github.com/EYALIN/community-cordova-plugin-file-opener

generally, you can use all my community Cordova plugins, I maintain them each time there is an update and expand some of them to support more features. here you can see all my Cordova plugins (suggest you move to my plugin in case you are using not maintained one): https://github.com/EYALIN?tab=repositories&q=community&type=&language=&sort=

appidemic1 commented 8 months ago

@Krantikc try to add logs and to see in log cat. from what i see, you will arrive here:

private boolean hasReadPermission() {
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            return PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_IMAGES) 
            && PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_VIDEO)
            && PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_AUDIO);
          } else {
            return PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
          }
    }

or here:

  private boolean hasWritePermission() {
          if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                return true;
              } else {
                return PermissionHelper.hasPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
              }
    }

please make sure you have those changes (for the write permission).

try remove ing the plugin and add it from my fork, i tried on few devices android 10-13 and all works. cordova plugin remove cordova-plugin-file cordova plugin add https://github.com/EYALIN/cordova-plugin-file.git

Thanks! @EYALIN https://github.com/EYALIN/cordova-plugin-file.git worked when i tried to write to cordova.file.externalRootDirectory + "Pictures/test/"

But i get error 2 (security error) when i try to read the files and list them cordova.file.externalRootDirectory + "Pictures/test/"

Any help would be great

ssnctracweb commented 7 months ago

This issue is major blocker for us as our apps are build using CI/CD pipelines and there's no way to patch it in run time builds. So any estimate by when this issue will be fixed for android version 13 and above ?

breautek commented 7 months ago

So any estimate by when this issue will be fixed for android version 13 and above ?

Because Apache Cordova is entirely driven by volunteers providing contributions on their free time, we can't really give any meaningful estimates. PR #581 requires changes and currently we are giving time for the OP to make those changes. If the OP happens to make those changes it may move quicker, otherwise someone will have to (volunteerly) pull the and rebase the fork and make any required changes necessary.

Should someone take the fork and rebase it, it's important to rebase it in such a way that the original author commit is left in tact so that credit is provided to the original author.

erisu commented 7 months ago

Should be resolved by https://github.com/apache/cordova-plugin-file/pull/608