MAUI安卓阅读器应用无法扫描模拟器中的FB2与TXT文件
解决MAUI安卓阅读器无法检索FB2/TXT文件的问题
问题核心分析
你遇到的问题源于两个关键点:
- MediaStore索引规则:PNG/JPG属于媒体文件会被自动索引,但FB2/TXT作为文档类,默认未被纳入常规索引范围,原筛选条件也存在大小写匹配漏洞
- Android分区存储限制:Android 10(API 29)及以上启用分区存储后,直接通过文件路径访问外部存储的方式会被系统限制,导致Java.IO/C# IO方法失效
针对性解决方案
方案1:修复MediaStore检索逻辑
调整筛选条件,加入文件类型判断和大小写兼容,确保FB2/TXT被正确匹配:
public static List<string> GetFilesFromMediaStore(string[] fileExtensions) { var result = new List<string>(); var uri = MediaStore.Files.GetContentUri("external"); // 构建精准筛选条件:仅匹配普通文件,且扩展名大小写不敏感 var conditions = new List<string>(); conditions.Add($"{MediaStore.Files.FileColumns.MediaType} = {MediaStore.Files.FileColumns.MediaTypeFile}"); foreach (var ext in fileExtensions) { conditions.Add($"LOWER({MediaStore.Files.FileColumns.Data}) LIKE '%.{ext.ToLower()}'"); } var selection = $"({string.Join(") AND (", conditions)})"; string[] projection = { MediaStore.Files.FileColumns.Data, MediaStore.Files.FileColumns.DisplayName }; using (var cursor = Android.App.Application.Context.ContentResolver.Query(uri, projection, selection, null, null)) { if (cursor != null && cursor.MoveToFirst()) { do { var filePath = cursor.GetString(cursor.GetColumnIndexOrThrow(MediaStore.Files.FileColumns.Data)); // 验证文件实际存在(避免MediaStore索引过时) if (Java.IO.File.Exists(filePath)) { result.Add(filePath); } } while (cursor.MoveToNext()); } cursor?.Close(); } return result; }
配套权限配置
在Platforms/Android/AndroidManifest.xml中添加:
<!-- Android 13及以下读取外部存储 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Android 13+读取文档类文件 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
方案2:适配分区存储的文件检索(Java.IO/C# IO)
Android 10+限制直接路径访问,需申请全存储权限(仅适用于文件管理器类应用,谷歌审核严格),修正后的代码如下:
权限配置
<!-- Android 10+全存储访问权限 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" />
修正后的C# IO检索代码
public static List<string> GetFilesCSharpIO(string rootPath) { var result = new List<string>(); try { if (!Directory.Exists(rootPath)) { Debug.WriteLine($"路径不存在: {rootPath}"); return result; } // 递归遍历并筛选指定扩展名 foreach (var file in Directory.EnumerateFiles(rootPath, "*.*", SearchOption.AllDirectories)) { var ext = Path.GetExtension(file).TrimStart('.').ToLower(); if (ext is "fb2" or "txt") { result.Add(file); } } } catch (UnauthorizedAccessException ex) { Debug.WriteLine($"权限不足: {ex.Message}"); } catch (Exception ex) { Debug.WriteLine($"文件访问错误: {ex.Message}"); } return result; }
方案3:推荐使用Storage Access Framework(SAF)
符合Android权限规范,无需敏感权限,让用户主动选择书籍目录:
启动目录选择器
private async Task PickBookFolderAsync() { var intent = new Intent(Intent.ActionOpenDocumentTree); intent.AddFlags(ActivityFlags.GrantReadUriPermission); intent.AddFlags(ActivityFlags.GrantPersistableUriPermission); intent.AddFlags(ActivityFlags.GrantPrefixUriPermission); var result = await Platform.CurrentActivity.StartActivityForResultAsync(intent, 100); if (result.ResultCode == Result.Ok && result.Data != null) { var folderUri = result.Data.Data; // 持久化权限,重启后仍可访问 Platform.CurrentActivity.ContentResolver.TakePersistableUriPermission(folderUri, ActivityFlags.GrantReadUriPermission); // 遍历目录下的FB2/TXT文件 var bookFiles = await TraverseSafFolderAsync(folderUri); // 处理获取到的文件列表 } }
遍历SAF目录
private async Task<List<string>> TraverseSafFolderAsync(Uri folderUri) { var result = new List<string>(); var childrenUri = DocumentsContract.BuildChildDocumentsUriUsingTree(folderUri, DocumentsContract.GetTreeDocumentId(folderUri)); using (var cursor = Platform.CurrentActivity.ContentResolver.Query(childrenUri, new[] { DocumentsContract.Document.ColumnDocumentId, DocumentsContract.Document.ColumnDisplayName, DocumentsContract.Document.ColumnMimeType }, null, null, null)) { if (cursor != null && cursor.MoveToFirst()) { do { var docId = cursor.GetString(cursor.GetColumnIndexOrThrow(DocumentsContract.Document.ColumnDocumentId)); var displayName = cursor.GetString(cursor.GetColumnIndexOrThrow(DocumentsContract.Document.ColumnDisplayName)); var mimeType = cursor.GetString(cursor.GetColumnIndexOrThrow(DocumentsContract.Document.ColumnMimeType)); if (mimeType == DocumentsContract.Document.MimeTypeDir) { // 递归遍历子目录 var childUri = DocumentsContract.BuildDocumentUriUsingTree(folderUri, docId); var childFiles = await TraverseSafFolderAsync(childUri); result.AddRange(childFiles); } else { var ext = Path.GetExtension(displayName).TrimStart('.').ToLower(); if (ext is "fb2" or "txt") { result.Add(displayName); // 读取文件内容示例: // using var stream = Platform.CurrentActivity.ContentResolver.OpenInputStream(folderUri); } } } while (cursor.MoveToNext()); } cursor?.Close(); } return result; }
内容的提问来源于stack exchange,提问作者6a9heast
相关产品推荐
相关产品推荐

