You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 04:28:14