Flutter安卓应用创建内部存储文件夹时遇权限错误
安卓存储权限错误:无法创建/storage/emulated/0/wizApp文件夹
错误信息
FileSystemException: Creation failed, path = '/storage/emulated/0/wizApp' (OS Error: Operation not permitted, errno = 1)
现有Dart代码
import 'dart:convert'; import 'dart:io'; import 'package:path_provider/path_provider.dart'; import 'package:flutter/material.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; // void exportData(List<Map<String, dynamic>> data) async { // final directory = await getExternalStorageDirectory(); // print('Download directory: ${directory.path}'); // Print the directory path // final file = File('${directory.path}/data.json'); // final jsonData = jsonEncode(data); // await file.writeAsString(jsonData); // print('Data exported successfully!'); // } Future<bool> saveData(List<Map<String, dynamic>> data, String fileName) async { Directory directory; try { if (Platform.isAndroid) { if (await _requestPermission(Permission.storage)) { directory = await getExternalStorageDirectory(); String newPath = ""; print(directory); List<String> paths = directory.path.split("/"); for (int x = 1; x < paths.length; x++) { String folder = paths[x]; if (folder != "Android") { newPath += "/" + folder; } else { break; } } newPath = newPath + "/wizApp"; directory = Directory(newPath); } else { return false; } } else { if (await _requestPermission(Permission.photos)) { directory = await getTemporaryDirectory(); } else { return false; } } File saveFile = File(directory.path + "/$fileName"); if (!await directory.exists()) { await directory.create(recursive: true); } if (await directory.exists()) { final file = File('${directory.path}/data.json'); final jsonData = jsonEncode(data); await file.writeAsString(jsonData); print('Data exported successfully!'); if (Platform.isIOS) { await ImageGallerySaver.saveFile(saveFile.path, isReturnPathOfIOS: true); } return true; } return false; } catch (e) { print(e); return false; } } Future<bool> _requestPermission(Permission permission) async { if (await permission.isGranted) { return true; } else { var result = await permission.request(); if (result == PermissionStatus.granted) { return true; } } return false; }
现有AndroidManifest配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.blockly"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> --> <application android:label="Wizklub IoT App" android:icon="@mipmap/ic_launcher" android:requestLegacyExternalStorage="true" android:usesCleartextTraffic="true"> <activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> <!-- Specifies an Android theme to apply to this Activity as soon as the Android process has started. This theme is visible to the user while the Flutter UI initializes. After that, this theme continues to determine the Window background behind the Flutter UI. --> <meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" /> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!-- Don't delete the meta-data below. This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> <meta-data android:name="flutterEmbedding" android:value="2" /> </application> </manifest>
解决方案
1. 适配Android 11+的全局存储权限
从Android 11开始,WRITE_EXTERNAL_STORAGE不再授予全局写入权限,要在根目录创建文件夹必须申请MANAGE_EXTERNAL_STORAGE权限:
- 取消注释AndroidManifest中的
MANAGE_EXTERNAL_STORAGE权限:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
- 修改权限请求逻辑,针对Android 11+请求
manageExternalStorage权限:
Future<bool> _requestPermission() async { if (Platform.isAndroid) { if (await Permission.manageExternalStorage.isGranted) { return true; } else { var result = await Permission.manageExternalStorage.request(); if (result == PermissionStatus.granted) { return true; } else { // 权限被拒绝时,引导用户去应用设置页开启 openAppSettings(); return false; } } } else { // iOS逻辑保持不变 if (await Permission.photos.isGranted) { return true; } else { var result = await Permission.photos.request(); return result == PermissionStatus.granted; } } }
2. 推荐:使用符合规范的存储路径(避免全局权限)
如果不需要让文件夹在应用卸载后保留,推荐使用应用专属外部存储目录,不需要任何额外权限:
Future<bool> saveData(List<Map<String, dynamic>> data, String fileName) async { Directory directory; try { if (Platform.isAndroid) { // 获取应用专属外部存储目录 directory = await getExternalFilesDirectory(null); // 在该目录下创建wizApp文件夹 directory = Directory('${directory.path}/wizApp'); } else { // iOS逻辑保持不变 if (await _requestPermission(Permission.photos)) { directory = await getTemporaryDirectory(); } else { return false; } } File saveFile = File(directory.path + "/$fileName"); if (!await directory.exists()) { await directory.create(recursive: true); } if (await directory.exists()) { final file = File('${directory.path}/data.json'); final jsonData = jsonEncode(data); await file.writeAsString(jsonData); print('Data exported successfully!'); if (Platform.isIOS) { await ImageGallerySaver.saveFile(saveFile.path, isReturnPathOfIOS: true); } return true; } return false; } catch (e) { print(e); return false; } }
这个目录的路径类似/storage/emulated/0/Android/data/com.example.blockly/files/wizApp,卸载应用时会自动删除,符合Android存储规范,无需申请权限。
3. 验证requestLegacyExternalStorage配置
确保android:requestLegacyExternalStorage="true"保留在application标签内,该配置兼容Android 10的存储行为。
内容的提问来源于stack exchange,提问作者Krishnaa Krishna
相关产品推荐
相关产品推荐

