tus / tus-android-client

The tus client for Android.
https://tus.io/
161 stars 46 forks source link

FileNotFoundException in Android 11 #44

Open mochadwi opened 3 years ago

mochadwi commented 3 years ago

https://developer.android.com/about/versions/11/behavior-changes-11

mochadwi commented 3 years ago

removing cursor line, fixes our issue:

class TusAndroidUpload(uri: Uri, context: Context) : TusUpload() {
    init {
        val resolver = context.contentResolver

        val fd = resolver.openFileDescriptor(uri, "r") ?: throw FileNotFoundException()
        val size = fd.statSize
        try {
            fd.close()
        } catch (e: IOException) {
            Log.e("TusAndroidUpload", "unable to close ParcelFileDescriptor", e)
        }
        setSize(size)
        inputStream = resolver.openInputStream(uri)
        fingerprint = String.format("%s-%d", uri.toString(), size)
        val metadata: MutableMap<String, String> = HashMap()
        metadata["filename"] = uri.path!!
        setMetadata(metadata)
    }
}
mochadwi commented 3 years ago

current cursor in Android 11 seems doesn't work properly and returning null instead :(

Acconut commented 3 years ago

Interesting report, thank you. Did you try enabling requestLegacyExternalStorage to get the behavior from Android 10 back (https://developer.android.com/about/versions/11/privacy/storage#scoped-storage)? This might be a temporary workaround until we find a solution to obtain the file name without a cursor.

Do you also have an alternative idea on how to obtain the file name?

mochadwi commented 3 years ago

Yes the requestLegacyExternalStorage is already added, but still fails >_<

We were still not aware of how the proper workaround using cursor currently 🙏 and still use the above approach instead.

mochadwi commented 3 years ago

For the filename, we're using our own Kotlin extension to get the filename from Uri. Lemme paste it here later (still afk)

mochadwi commented 3 years ago
fun Uri.fileName() = "$path".substring("$path".lastIndexOf("/") + 1)

This is a hackish ways though, we assume the last index as a file name from the suffix "/".

Any better suggestions?

In Java I think it's a little bit verbose than Kotlin version

Acconut commented 3 years ago

Yes the requestLegacyExternalStorage is already added, but still fails >_<

Too bad, thanks for clarifying!

For the filename, we're using our own Kotlin extension to get the filename from Uri

I can see that this works for simple file selections, but might break when wanting to upload a file shared from another app. In this cases, the URI might be content://media/external/audio/media/710 where the last path segment does not correspond to the filename. We used the cursor to lookup the filename for this external content but apparently this is no forbidden since we are trying to access files from another app. See https://stackoverflow.com/a/27926504

I do not spend much time developing for Android these days, so I am not sure if there exists another approach for getting the filename. If not, it would be the best solution to just leave the filename empty if we get an FileNotFoundException. This way, the error is handled as gracefully as we can. Would you be interested in a PR for this?

mochadwi commented 3 years ago

Will check the alternative ways to get the filename via cursor gracefully for Android 11 above, and then hopefully will submit the PR around end of month or early next month 🙏

Acconut commented 3 years ago

Thank you! Looking forward to your contribution :)

mochadwi commented 3 years ago

https://twitter.com/yrezgui/status/1405194690811596808?s=19

There's a modernstorage library by Google, something to look for after the release.

Because it's a tus-android is it okay to coupled with the android-related library? 🙏

Acconut commented 3 years ago

There's a modernstorage library by Google, something to look for after the release.

Definitely! The modernstorage library definitely looks promising! Thanks for sharing

Because it's a tus-android is it okay to coupled with the android-related library?

Yes, exactly. tus-android-client is meant to contain Android-specific code :)