You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter在Android 13中访问OTG挂载二级存储文件的方案咨询

在Android 13的Flutter应用中访问OTG挂载存储(/mnt/media_rw/)

由于path_provider、external_path等Flutter包仅能访问内部存储或应用私有目录,无法获取OTG这类可移动存储的挂载路径(如/mnt/media_rw/),需通过原生Android代码配合MethodChannel实现访问,以下是具体方案:

1. 配置Android权限

编辑android/app/src/main/AndroidManifest.xml,添加必要权限:

<!-- 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" />

<!-- 兼容Android 12及以下版本的外部存储读取权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />

2. 通过MethodChannel获取OTG挂载路径

Android端原生代码(Kotlin)

在MainActivity.kt中实现挂载路径获取逻辑,并注册MethodChannel:

import android.os.Environment
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
    private val CHANNEL = "com.your_app_id/otg_storage"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            when (call.method) {
                "getOTGMountPaths" -> result.success(getOTGMountPaths())
                "checkManageStoragePermission" -> result.success(Environment.isExternalStorageManager())
                "getPackageName" -> result.success(packageName)
                else -> result.notImplemented()
            }
        }
    }

    private fun getOTGMountPaths(): List<String> {
        val mountPaths = mutableListOf<String>()
        val storageManager = getSystemService(STORAGE_SERVICE) as android.os.storage.StorageManager
        // 遍历所有存储卷,筛选已挂载的可移动存储
        storageManager.storageVolumes.forEach { volume ->
            if (volume.isRemovable && volume.state == android.os.storage.StorageVolume.STATE_MOUNTED) {
                volume.directory?.absolutePath?.let { mountPaths.add(it) }
            }
        }
        return mountPaths
    }
}

Flutter端调用代码

创建工具类调用原生方法获取路径:

import 'package:flutter/services.dart';
import 'package:android_intent_plus/android_intent.dart';

class OTGStorageHelper {
    static const MethodChannel _channel = MethodChannel('com.your_app_id/otg_storage');

    // 获取OTG挂载路径列表
    static Future<List<String>?> getOTGMountPaths() async {
        try {
            final List<dynamic> result = await _channel.invokeMethod('getOTGMountPaths');
            return result.cast<String>();
        } on PlatformException catch (e) {
            print("获取OTG路径失败: ${e.message}");
            return null;
        }
    }

    // 检查是否拥有全文件访问权限
    static Future<bool> checkManageStoragePermission() async {
        try {
            return await _channel.invokeMethod('checkManageStoragePermission');
        } on PlatformException catch (e) {
            print("权限检查失败: ${e.message}");
            return false;
        }
    }

    // 跳转到应用权限设置页面(申请全文件访问权限)
    static Future<void> openStoragePermissionSettings() async {
        try {
            String packageName = await _channel.invokeMethod('getPackageName');
            final intent = AndroidIntent(
                action: 'android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION',
                data: Uri.parse('package:$packageName'),
            );
            await intent.launch();
        } on PlatformException catch (e) {
            print("打开设置失败: ${e.message}");
        }
    }
}

// 使用示例
void fetchOTGPaths() async {
    bool hasPermission = await OTGStorageHelper.checkManageStoragePermission();
    if (!hasPermission) {
        await OTGStorageHelper.openStoragePermissionSettings();
        return;
    }
    List<String>? otgPaths = await OTGStorageHelper.getOTGMountPaths();
    if (otgPaths != null) {
        print("OTG挂载路径: $otgPaths");
        // 输出示例: [/mnt/media_rw/USB1]
    }
}

3. 读写OTG存储文件

获取路径后,直接使用dart:io的File类进行读写操作:

import 'dart:io';

// 写入文件到OTG
Future<void> writeToOTG() async {
    List<String>? otgPaths = await OTGStorageHelper.getOTGMountPaths();
    if (otgPaths != null && otgPaths.isNotEmpty) {
        String targetPath = "${otgPaths.first}/test_otg.txt";
        try {
            await File(targetPath).writeAsString("Flutter OTG 写入测试");
            print("文件写入成功");
        } catch (e) {
            print("写入失败: $e");
        }
    }
}

// 从OTG读取文件
Future<void> readFromOTG() async {
    List<String>? otgPaths = await OTGStorageHelper.getOTGMountPaths();
    if (otgPaths != null && otgPaths.isNotEmpty) {
        String targetPath = "${otgPaths.first}/test_otg.txt";
        try {
            String content = await File(targetPath).readAsString();
            print("读取到内容: $content");
        } catch (e) {
            print("读取失败: $e");
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 06:30:44