Skip to content

[fs] On Android I cannot extract filename returned by open / pick_file(s) #1775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
micouy opened this issue Sep 13, 2024 · 2 comments · Fixed by tauri-apps/tauri#13012
Closed
Labels
plugin: dialog plugin: fs Includes former "fs-extra" and "fs-watch" plugins question Further information is requested

Comments

@micouy
Copy link

micouy commented Sep 13, 2024

When I select files on Android (either by open on frontend or pick_files on backend) a content:// URI is returned:

content://com.android.providers.media.documents/document/image%3A18

How do I get to the filename? Should I use some resolver or open the file some other way?

@aiueo13
Copy link

aiueo13 commented Sep 17, 2024

Resolved? I'll make a temp plugin for you if you need it.

@FabianLars FabianLars added question Further information is requested plugin: dialog plugin: fs Includes former "fs-extra" and "fs-watch" plugins labels Sep 17, 2024
@micouy
Copy link
Author

micouy commented Sep 18, 2024

I got it partially working by fetching type and name while opening the file descriptor in FsPlugin.kt:

    fun getFileDescriptor(invoke: Invoke) {
        val args = invoke.parseArgs(GetFileDescriptorArgs::class.java)

        val res = JSObject()

        if (args.uri.startsWith(app.tauri.TAURI_ASSETS_DIRECTORY_URI)) {
            val path = args.uri.substring(app.tauri.TAURI_ASSETS_DIRECTORY_URI.length)
            try {
                val fd = activity.assets.openFd(path).parcelFileDescriptor?.detachFd()
                res.put("fd", fd)
                // Get MIME type
                // val mimeType =
                //
                // activity.contentResolver.getType(Uri.parse("file:///android_asset/$path"))
                val mimeType = activity.contentResolver.getType(Uri.parse(args.uri))
                res.put("mimeType", mimeType ?: "application/octet-stream")

                // Get file name
                val fileName = Uri.parse(args.uri).lastPathSegment
                res.put("fileName", fileName)
                res.put("branch", "uncompressed asset")
            } catch (e: IOException) {
                // if the asset is compressed, we cannot open a file descriptor directly
                // so we copy it to the cache and get a fd from there
                // this is a lot faster than serializing the file and sending it as invoke response
                // because on the Rust side we can leverage the custom protocol IPC and read the
                // file directly
                val cacheFile = File(activity.cacheDir, "_assets/$path")
                cacheFile.parentFile?.mkdirs()
                copyAsset(path, cacheFile)

                val fd =
                        ParcelFileDescriptor.open(
                                        cacheFile,
                                        ParcelFileDescriptor.parseMode(args.mode)
                                )
                                .detachFd()
                res.put("fd", fd)

                // Get MIME type
                val mimeType = activity.contentResolver.getType(Uri.fromFile(cacheFile))
                res.put("mimeType", mimeType ?: "application/octet-stream")

                // Get file name
                val fileName = Uri.fromFile(cacheFile).lastPathSegment
                res.put("fileName", fileName)
                res.put("branch", "compressed asset $e")
            }
        } else {
            val mimeType = activity.contentResolver.getType(Uri.parse(args.uri))
            val fileName =
                    activity.contentResolver.query(Uri.parse(args.uri), null, null, null, null)
                            ?.use { cursor ->
                                if (cursor.moveToFirst()) {
                                    val nameIndex =
                                            cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)

                                    if (nameIndex != -1) {
                                        cursor.getString(nameIndex)
                                    } else {
                                        null
                                    }
                                } else {
                                    null
                                }
                            }
                            ?: Uri.parse(args.uri).lastPathSegment
            val fd =
                    activity.contentResolver
                            .openAssetFileDescriptor(Uri.parse(args.uri), args.mode)
                            ?.parcelFileDescriptor
                            ?.detachFd()
            res.put("fd", fd)

            res.put("mimeType", mimeType)
            res.put("fileName", fileName)
            res.put("branch", "not asset")
        }

        invoke.resolve(res)
    }

However, I'm still interested if and when such functionality is going to be added to the plugin.

lucasfernog added a commit to tauri-apps/tauri that referenced this issue Mar 17, 2025
This PR adds a new Android path plugin function to resolve file names from content URIs. `PathResolver::file_name` was added to expose this API on Rust, and the existing `@tauri-apps/api/path` basename and extname function now leverages it on Android.

Closes tauri-apps/plugins-workspace#1775

Tauri core port from tauri-apps/plugins-workspace#2421

Co-authored-by: VulnX
kandrelczyk pushed a commit to kandrelczyk/tauri that referenced this issue Apr 7, 2025
…13012)

* feat(core): resolve file names from Android content URIs

This PR adds a new Android path plugin function to resolve file names from content URIs. `PathResolver::file_name` was added to expose this API on Rust, and the existing `@tauri-apps/api/path` basename and extname function now leverages it on Android.

Closes tauri-apps/plugins-workspace#1775

Tauri core port from tauri-apps/plugins-workspace#2421

Co-authored-by: VulnX

* update change file [skip ci]

Co-authored-by: VulnX <[email protected]>

---------

Co-authored-by: VulnX <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
plugin: dialog plugin: fs Includes former "fs-extra" and "fs-watch" plugins question Further information is requested
Projects
None yet
3 participants