无需READ_MEDIA_IMAGES权限,Flutter如何列出FilePicker选中目录的文件?
无需READ_MEDIA_IMAGES权限列出FilePicker选中目录的媒体文件?
我当前的实现是用flutter_file_picker让用户选择目录,再列出其中媒体文件,代码如下:
String? selectedDirectory = await FilePicker.platform.getDirectoryPath(); if (selectedDirectory != null) { final files = Directory(selectedDirectory).list().toList(); log(files.toString()); }
原本申请READ_MEDIA_IMAGES权限后功能正常,但根据Google Play 2024年中政策,需要移除该权限改用系统选择器,移除后仅能访问应用自身创建的文件,Android 13/14下无法读取目录中的其他媒体文件,请问有没有无需权限的实现方式?
解决方案:利用SAF(存储访问框架)实现无权限访问
Android 13+的存储权限机制限制了直接文件系统访问公共目录的媒体文件,但通过SAF获取的目录URI会带有系统临时授予的访问权限,可通过以下方式实现:
- 启用flutter_file_picker的SAF模式
修改目录选择的调用代码,开启SAF模式,此时返回的是content://开头的URI(而非本地文件路径),带有临时访问权限:
String? selectedDirectory = await FilePicker.platform.getDirectoryPath( useSAF: true, // 关键:启用SAF模式 );
- 通过SAF API遍历目录下的媒体文件
使用document_file_preview这类封装了SAF操作的插件,直接遍历选中目录下的文件并筛选媒体类型:
import 'package:document_file_preview/document_file_preview.dart'; if (selectedDirectory != null) { Uri directoryUri = Uri.parse(selectedDirectory); // 遍历目录下所有文件 List<DocumentFile> allFiles = await DocumentFile.listFiles(uri: directoryUri); // 筛选图片/视频类媒体文件 List<DocumentFile> mediaFiles = allFiles.where((file) { String? mimeType = file.mimeType; return mimeType?.startsWith('image/') == true || mimeType?.startsWith('video/') == true; }).toList(); // 输出媒体文件信息 for (var media in mediaFiles) { log('媒体文件:${media.name},路径:${media.uri}'); } }
- 或通过MediaStore查询指定目录的媒体文件
如果你更倾向于用系统媒体库查询,可通过MediaStore结合SAF URI的路径匹配来获取目标目录下的媒体:
import 'package:media_store/media_store.dart'; if (selectedDirectory != null) { Uri directoryUri = Uri.parse(selectedDirectory); // 提取目录的相对路径用于匹配 String targetPath = directoryUri.path.replaceFirst('/document/primary:', ''); // 查询图片文件 final imageCursor = await MediaStore.query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns: [MediaStore.Images.Media.DATA, MediaStore.Images.Media.DISPLAY_NAME], where: "${MediaStore.Images.Media.RELATIVE_PATH} LIKE ?", whereArgs: ['$targetPath%'], ); while (imageCursor.moveToNext()) { String fileName = imageCursor.getString(imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)); String filePath = imageCursor.getString(imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)); log('图片:$fileName,路径:$filePath'); } imageCursor.close(); // 同理可查询视频文件,替换为MediaStore.Videos.Media相关常量即可 }
关键说明
- 不要使用
Directory.list()直接访问:这属于直接文件系统操作,无权限时仅能访问应用私有目录,无法读取公共目录的媒体文件。 - SAF的临时权限:通过
useSAF: true获取的URI带有系统授予的临时访问权限,足够完成本次目录遍历操作,无需永久存储权限。 - 符合Google Play政策:这种方式不需要申请
READ_MEDIA_IMAGES/READ_MEDIA_VIDEO权限,完全符合政策中"改用系统选择器"的要求。
内容的提问来源于stack exchange,提问作者Noah
相关产品推荐
相关产品推荐

