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
相关产品推荐
相关产品推荐

