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

MAUI:Android模拟器中FileSaver存SD卡却返回内置存储路径

解决CommunityToolkit.Maui FileSaver返回SD卡文件路径错误的问题

当使用CommunityToolkit.Maui.Storage.FileSaver在Android API32上将文件保存到SD卡时,返回的FilePath始终指向内置存储,而非实际保存的SD卡路径,导致后续操作不符合预期。以下是针对该问题的解决方案:

核心原因

CommunityToolkit.Maui的FileSaver在处理Android存储访问框架(SAF)返回的Uri时,未能正确解析SD卡的真实路径,导致返回内置存储的模拟路径。

解决方案:自定义文件保存逻辑(平台特定实现)

绕过CommunityToolkit的FileSaver,直接调用Android原生的SAF来获取真实的文件路径,具体步骤如下:

1. 定义跨平台接口

在共享项目中创建一个接口,统一文件保存的调用方式:

public interface ICustomFileSaver
{
    Task<string> SaveFileAsync(string initialFileName, Stream stream, CancellationToken cancellationToken);
}

2. Android平台实现

在Android项目中实现上述接口,处理SAF的文件保存和路径解析:

[assembly: Dependency(typeof(CustomFileSaver))]
namespace YourAppNamespace.Platforms.Android;

using Android.Content;
using Android.OS;
using Android.Provider;
using Microsoft.Maui.ApplicationModel;

public class CustomFileSaver : ICustomFileSaver
{
    public async Task<string> SaveFileAsync(string initialFileName, Stream stream, CancellationToken cancellationToken)
    {
        // 创建保存文件的Intent
        var saveIntent = new Intent(Intent.ActionCreateDocument);
        saveIntent.AddCategory(Intent.CategoryOpenable);
        saveIntent.SetType("*/*"); // 根据文件类型调整,例如"text/plain"或"image/png"
        saveIntent.PutExtra(Intent.ExtraTitle, initialFileName);

        // 启动系统文件选择器并等待结果
        var activityResult = await Platform.CurrentActivity.StartActivityForResultAsync(saveIntent, 1001);

        if (activityResult.ResultCode == Result.Ok && activityResult.Data != null)
        {
            var fileUri = activityResult.Data.Data;
            // 将流写入选中的文件
            using var outputStream = Platform.CurrentActivity.ContentResolver.OpenOutputStream(fileUri);
            await stream.CopyToAsync(outputStream, cancellationToken);

            // 解析Uri对应的真实SD卡路径
            return ResolveRealPathFromUri(fileUri);
        }

        return null;
    }

    private string ResolveRealPathFromUri(Android.Net.Uri uri)
    {
        string realPath = null;
        var contentResolver = Platform.CurrentActivity.ContentResolver;

        // 尝试从MediaStore获取路径
        using var cursor = contentResolver.Query(uri, null, null, null, null);
        if (cursor != null && cursor.MoveToFirst())
        {
            var dataColumnIndex = cursor.GetColumnIndex(MediaStore.MediaColumns.Data);
            if (dataColumnIndex != -1)
            {
                realPath = cursor.GetString(dataColumnIndex);
            }
        }

        // 处理SD卡的Uri格式
        if (string.IsNullOrEmpty(realPath) && uri.Scheme.Equals("content", StringComparison.OrdinalIgnoreCase))
        {
            var docId = DocumentsContract.GetDocumentId(uri);
            var splitDocId = docId.Split(':');
            var storageType = splitDocId[0];

            if ("primary".Equals(storageType, StringComparison.OrdinalIgnoreCase))
            {
                realPath = $"{Android.OS.Environment.ExternalStorageDirectory.AbsolutePath}/{splitDocId[1]}";
            }
            else
            {
                // 遍历所有外部存储目录,匹配SD卡路径
                var externalDirs = Android.OS.Environment.GetExternalStorageDirs();
                foreach (var dir in externalDirs)
                {
                    var dirPath = dir.AbsolutePath;
                    if (dirPath.Contains(storageType))
                    {
                        realPath = $"{dirPath}/{splitDocId[1]}";
                        break;
                    }
                }
            }
        }

        return realPath;
    }
}

3. 在共享项目中调用

替换原有的FileSaver调用,使用自定义的文件保存逻辑:

var customSaver = DependencyService.Get<ICustomFileSaver>();
var actualFilePath = await customSaver.SaveFileAsync(fileName, stream, ct);

if (!string.IsNullOrEmpty(actualFilePath))
{
    filename = actualFilePath;
    // 后续基于真实路径的操作
}

注意事项

  • 确保已在AndroidManifest.xml中申请MANAGE_EXTERNAL_STORAGE权限,并完成用户授权流程。
  • 根据实际文件类型调整SetType参数,避免系统文件选择器过滤掉目标文件类型。
  • 不同厂商的SD卡挂载路径可能存在差异,需在多设备上测试路径解析逻辑的兼容性。

内容的提问来源于stack exchange,提问作者user7399085

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 00:15:17