You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

基于Tauri的Android应用如何申请主目录全文件访问权限?

Android Tauri应用请求全存储权限问题排查

在现代Android系统中,使用Tauri(Rust后端+JavaScript前端)开发应用时,如何请求用户授予访问主目录(/storage/emulated/0)下所有文件(包括文档、下载、图片、视频及外部存储设备文件)的权限?

我尝试了以下代码,各平台编译无错误警告,应用在Android上运行正常无崩溃,但始终不会触发权限请求,导致无法读取或列出大量必要文件。请问问题出在哪里?

原代码示例

Rust后端代码

// rust side
...
#[cfg(target_os = "android")]
use tauri_plugin_android_fs::{AndroidFsExt};
...
tauri::Builder::default()
        .plugin(tauri_plugin_android_fs::init())
...
// this is the func to let the app ask the user for minimal storage access
#[tauri::command]
async fn request_directory_access(app: tauri::AppHandle) {
    #[cfg(target_os = "android")]
    {
        let fs = app.android_fs_async();
        let file_picker = fs.file_picker();
        let _ = file_picker
            .pick_dir(
                None,  // no initial location hint
                true,  // local only (no cloud storage)
            )
            .await
            .map_err(|e| e.to_string());
    }
}

JavaScript前端代码

// javascript side
...
// request file access permission if android
window.addEventListener("DOMContentLoaded", async () => {
    try {
        await invoke("request_directory_access");
    } catch (err) {
        console.error(err);
    }
});
...

问题根源及解决方案

1. 核心问题:权限逻辑与需求不匹配

你当前调用的pick_dir文件选择器API,它的作用是让用户手动选择单个目录,应用仅能访问该目录及子内容,不会触发全局存储权限请求,完全不符合你"访问主目录下所有文件"的需求。

2. 必要的权限声明缺失

Android 10+对存储权限管控严格,必须在AndroidManifest.xml中添加对应权限:

<!-- Android 12及以下的全局存储读取权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Android 13+ 媒体文件细分权限 -->
<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" />
<!-- Android 10+ 访问所有文件的特殊权限(需用户手动在设置开启) -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    android:minSdkVersion="30" />

3. 正确的权限请求实现

Rust后端修改版

#[cfg(target_os = "android")]
use tauri::Manager;
#[cfg(target_os = "android")]
use android_activity::AndroidApp;
#[cfg(target_os = "android")]
use android_system_properties::AndroidSystemProperties;

#[tauri::command]
async fn request_full_storage_access(app: tauri::AppHandle) -> Result<String, String> {
    #[cfg(target_os = "android")]
    {
        let android_app = app.android_app().ok_or("无法获取Android应用句柄")?;
        let sdk_version = AndroidSystemProperties::get_int("ro.build.version.sdk", 0)?;

        if sdk_version >= 30 {
            // Android 10+ 检查全存储权限
            if !android_app.has_permission("android.permission.MANAGE_EXTERNAL_STORAGE") {
                // 引导用户到应用设置页开启特殊权限
                android_app.open_app_settings()?;
                return Ok("请在设置中开启「所有文件访问权限」".to_string());
            }
        } else if sdk_version >= 23 {
            // Android 6-9 请求传统存储权限
            let result = android_app.request_permissions(&["android.permission.READ_EXTERNAL_STORAGE"]).await?;
            if !result["android.permission.READ_EXTERNAL_STORAGE"].granted() {
                return Err("存储权限被拒绝".to_string());
            }
        }
        Ok("权限已获取".to_string())
    }
    #[cfg(not(target_os = "android"))]
    Ok("非Android平台无需权限".to_string())
}

JavaScript前端修改版

window.addEventListener("DOMContentLoaded", async () => {
    try {
        const res = await invoke("request_full_storage_access");
        console.log(res);
    } catch (err) {
        console.error("权限请求失败:", err);
    }
});

4. 关键注意事项

  • MANAGE_EXTERNAL_STORAGE是特殊权限,无法通过弹窗直接请求,必须引导用户到应用设置页手动开启
  • 若仅需访问媒体文件(图片/视频/音频),优先使用READ_MEDIA_*权限,避免申请全存储权限(Google Play审核可能卡审)
  • 测试时必须用真实设备验证,部分模拟器的权限模拟存在偏差

内容的提问来源于stack exchange,提问作者anis djerrab

火山引擎 最新活动