卸载重装Flutter应用后无法访问Download目录SQLite数据库的解决方法
问题描述
SQLite数据库存储路径为/storage/emulated/0/Download/Database,初始使用正常,但卸载重装应用后访问数据库时触发权限拒绝错误:
/storage/emulated/0/DATABASE/tablet_pos.db (OS Error: Permission denied, errno = 13)
已确认文件存在且路径、文件名无误,目录包含tablet_pos.db和tablet_pos.journal两个文件。附数据库初始化代码:
Future<void> initDatabase() async { if (Platform.isAndroid) { databasesPath = '/storage/emulated/0/Download/DATABASE'; // storageDirectory = await getExternalStorageDirectory(); // databasesPath = p.join(storageDirectory.path, FolderNames.database); } if (!await Directory(databasesPath).exists()) { await Directory(databasesPath).create(recursive: true); } var path = p.join(databasesPath, 'tablet_pos.db'); print('databasesPath $databasesPath $path'); var databaseFile = File(path); if (!await databaseFile.exists()) { try { print('await $database $path'); await databaseFile.create(recursive: true); } catch (e) { print('Error creating database file: $e'); return; } } // Check if the database file exists if (!await databaseFile.exists()) { print('Database file not found'); return; } print('try $database $path'); // Attempt to read the file to verify accessibility try { await databaseFile.readAsBytes(); } catch (e) { print('Database file is not readable: $e'); // error here on reinstall return; } database = await openDatabase(path, version: 1, onCreate: (Database db, int version) async { await db.execute(CustomerDB.createTable); }); print('database $database $path'); }
解决方法
1. 重新申请存储权限
卸载应用后,之前授予的权限会被清空,必须重新请求公共存储访问权限:
- 在
AndroidManifest.xml中添加权限声明:<!-- Android 10及以下所需权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Android 11+ 访问Download目录专属权限 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> - 用
permission_handler包动态申请权限,在初始化数据库前校验:
调用后若权限未授予,可引导用户前往设置开启。import 'package:permission_handler/permission_handler.dart'; Future<bool> checkStoragePermission() async { PermissionStatus status; if (Platform.isAndroid) { status = await (await AndroidInfo.version >= 30 ? Permission.manageExternalStorage.request() : Permission.storage.request()); return status.isGranted; } return true; }
2. 适配Android分区存储(Android 10+)
Android 10起的分区存储限制了绝对路径访问公共目录,建议用**存储访问框架(SAF)**获取持久化目录权限:
- Flutter侧调用原生方法打开目录选择器:
import 'package:flutter/services.dart'; Future<String?> getPersistentDirAccess() async { const MethodChannel channel = MethodChannel('storage_access'); try { return await channel.invokeMethod('openDirPicker'); } on PlatformException catch (e) { print('选目录出错: $e'); return null; } } - Android原生侧实现目录选择与权限持久化(MainActivity中):
获取URI后,通过ContentResolver操作文件,替代直接使用File类。private val CHANNEL = "storage_access" private val REQUEST_DIR = 1001 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "openDirPicker" -> { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) startActivityForResult(intent, REQUEST_DIR) result.success(null) } else -> result.notImplemented() } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_DIR && resultCode == RESULT_OK) { data?.data?.let { uri -> // 持久化权限,重启、重装后仍有效 contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) // 将URI传回Flutter MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL) .invokeMethod("dirSelected", uri.toString()) } } }
3. 避免硬编码路径
不要直接写/storage/emulated/0/Download/DATABASE,改用Flutter路径API获取公共目录:
import 'package:path_provider/path_provider.dart'; Future<String> getDownloadDir() async { if (Platform.isAndroid) { // Android 10+ 推荐用SAF,旧版本可兼容以下写法 return (await getExternalStorageDirectory())?.parent?.parent?.parent?.parent?.path ?? ''; } return (await getApplicationDocumentsDirectory()).path; }
4. 尝试修改文件权限
若数据库文件由旧应用创建,新应用UID不同可能无读写权限,可尝试修改文件权限:
await databaseFile.chmod(0666); // 赋予所有用户读写权限
注:Android 10+分区存储下该方法可能受限,仅作补充方案。
总结
优先选择申请MANAGE_EXTERNAL_STORAGE权限(Android 11+)或动态存储权限(Android 10及以下);若需严格适配分区存储,就用**存储访问框架(SAF)**获取持久化目录权限,确保卸载重装后仍能访问目标数据库。
内容的提问来源于stack exchange,提问作者VDTe
相关产品推荐
相关产品推荐

