ionic-team / capacitor-plugins

Official plugins for Capacitor ⚡️
512 stars 581 forks source link

bug: Filesystem.stat() crashes app with content urls #308

Open ThomasKientz opened 4 years ago

ThomasKientz commented 4 years ago

Bug Report

Capacitor Version

npx cap doctor output:

  @capacitor/cli 2.0.1
  @capacitor/core 2.0.1
  @capacitor/android 2.0.1

Affected Platform(s)

Description

const data = await Filesystem.readFile({
  path,
})
const { size } = await Filesystem.stat({
  path,
})

No issues with Filesystem.readFile

With Filesystem.stat the app crashed and I got the following error in the Android Studio console

2020-04-29 17:06:28.150 6188-6388/com.boomerang.app V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 77806689, pluginId: Filesystem, methodName: stat
2020-04-29 17:06:28.150 6188-6388/com.boomerang.app V/Capacitor: callback: 77806689, pluginId: Filesystem, methodName: stat, methodData: {"path":"content:\/\/com.android.providers.media.documents\/document\/image%3A24"}
2020-04-29 17:06:28.152 6188-6340/com.boomerang.app E/Capacitor: Serious error executing plugin
    java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
        at com.getcapacitor.Bridge$1.run(Bridge.java:520)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
        at com.getcapacitor.plugin.Filesystem.getDirectory(Filesystem.java:67)
        at com.getcapacitor.plugin.Filesystem.getFileObject(Filesystem.java:90)
        at com.getcapacitor.plugin.Filesystem.stat(Filesystem.java:440)
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99) 
        at com.getcapacitor.Bridge$1.run(Bridge.java:520) 
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
2020-04-29 17:06:28.153 6188-6340/com.boomerang.app E/AndroidRuntime: FATAL EXCEPTION: CapacitorPlugins
    Process: com.boomerang.app, PID: 6188
    java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
        at com.getcapacitor.Bridge$1.run(Bridge.java:529)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
        at com.getcapacitor.Bridge$1.run(Bridge.java:520)
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
        at com.getcapacitor.plugin.Filesystem.getDirectory(Filesystem.java:67)
        at com.getcapacitor.plugin.Filesystem.getFileObject(Filesystem.java:90)
        at com.getcapacitor.plugin.Filesystem.stat(Filesystem.java:440)
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99) 
        at com.getcapacitor.Bridge$1.run(Bridge.java:520) 
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
jcesarmobile commented 4 years ago

can you provide a sample app?

ThomasKientz commented 4 years ago

@jcesarmobile I made further tests. Crash only occurs with paths starting content://. I got those paths from an intent (https://github.com/darryncampbell/darryncampbell-cordova-plugin-intent#intentshimonintent)

Here is a sample app : https://github.com/ThomasKientz/capacitor-fs-issue-repro

jcesarmobile commented 4 years ago

Yeah, content urls are only supported by readFile at the moment because they are not regular filesystem url, they belong to other app and you can't do Filesystem tasks on another app file, other than reading it if it was shared, and maybe stat could be implemented with a lot of content resolver code. But should definitely not crash.

ThomasKientz commented 4 years ago

I have created a plugin to read a file's name and size from a content uri : https://github.com/ThomasKientz/capacitor-plugin-file-infos

Would be nice to implement it inside FS plugin imo !

tobiasmuecksch commented 4 years ago

Same issue with the copyFile method. Works perfectly on iOS but not on Android....

jcesarmobile commented 4 years ago

This issue is about content urls, that's an android thing, so can't work on iOS. If you mean copyFile doesn't work on Android, report a new issue and provide more information.

tobiasmuecksch commented 4 years ago

What I mean is, that Filesystem.copy()doesn't work with content:// urls too. This issue doesn't only effect the Filesystem.stat() method. My iOS comment is about my expectation, that I can use the same Ionic-App-code for android and iOS. That means passing a Filesystem URI (which I got by some plugin) to any of the FileSystem plugin methods and the Filesystem plugin just does it's magic as it is supposed too. But as often, I have to provide some extra code for Android.... As far as I can tell, in order to copy a file from a content:// URI I'd have to read the file as base64 and then write it to the data-directory for example. That is tremendous overhead which I'd feel very uncomfortable with. Is there any other way?

tobiasmuecksch commented 4 years ago

I think important information like that, should be displayed way more prominent in the docs. The current hint is not sufficiently clear at all.

Additionally, the Filesystem API supports using full file:// paths, or reading content:// files on Android. Simply leave out the directory param to use a full file path. Source: https://capacitorjs.com/docs/apis/filesystem#understanding-directories-and-files

tobiasmuecksch commented 4 years ago

I found a solution / workaround.

I'm now converting content:// URLs to file:// with the following plugin:

https://ionicframework.com/docs/native/file-path