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

Xamarin Forms Android 10及以上按文件名查找公共外部存储视频并获取路径

解决方案

前置权限配置

首先确保应用已经申请对应存储权限:

  • Android 12及以下:申请READ_EXTERNAL_STORAGE权限
  • Android 13及以上:申请READ_MEDIA_VIDEO权限

核心实现逻辑

你可以通过MediaStore查询公共媒体库,传入文件名匹配你存储的视频,同时可以额外匹配你自定义的存储目录避免同名文件冲突。以下是安卓端的具体实现,你可以通过Xamarin.Forms的DependencyService跨平台调用:

1. 公共项目定义接口

public interface IVideoService
{
    /// <summary>
    /// 通过文件名获取可直接用于播放的视频Uri
    /// </summary>
    Task<Android.Net.Uri> GetVideoUriByFileName(string fileName);
    /// <summary>
    /// 通过文件名获取视频本地绝对路径(仅Android 9及以下可靠,高版本优先用Uri播放)
    /// </summary>
    Task<string> GetVideoPathByFileName(string fileName);
}

2. 安卓平台实现接口

[assembly: Dependency(typeof(VideoService_Android))]
namespace YourAppNamespace.Droid
{
    public class VideoService_Android : IVideoService
    {
        private readonly string _targetDirectory = "DCIM/KiloFürKilo";
        private readonly string _targetDirectoryLegacy = Path.Combine(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryMovies).AbsolutePath, "KiloFürKilo");

        public async Task<Android.Net.Uri> GetVideoUriByFileName(string fileName)
        {
            return await Task.Run(() =>
            {
                var context = CrossCurrentActivity.Current.AppContext;
                string[] projection = { MediaStore.Video.Media.InterfaceConsts.Id, MediaStore.Video.Media.InterfaceConsts.DisplayName, MediaStore.Video.Media.InterfaceConsts.RelativePath, MediaStore.Video.Media.InterfaceConsts.Data };
                string selection = $"{MediaStore.Video.Media.InterfaceConsts.DisplayName} = ?";
                string[] selectionArgs = { fileName };

                using var cursor = context.ContentResolver.Query(MediaStore.Video.Media.ExternalContentUri, projection, selection, selectionArgs, null);
                if (cursor == null || !cursor.MoveToFirst()) return null;

                // 匹配存储目录避免同名文件
                var relativePath = cursor.GetString(cursor.GetColumnIndexOrThrow(MediaStore.Video.Media.InterfaceConsts.RelativePath));
                var filePath = cursor.GetString(cursor.GetColumnIndexOrThrow(MediaStore.Video.Media.InterfaceConsts.Data));
                if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Q)
                {
                    if (!relativePath.TrimEnd('/').Equals(_targetDirectory, StringComparison.OrdinalIgnoreCase))
                        return null;
                }
                else
                {
                    if (!Path.GetDirectoryName(filePath).Equals(_targetDirectoryLegacy, StringComparison.OrdinalIgnoreCase))
                        return null;
                }

                var id = cursor.GetLong(cursor.GetColumnIndexOrThrow(MediaStore.Video.Media.InterfaceConsts.Id));
                return ContentUris.WithAppendedId(MediaStore.Video.Media.ExternalContentUri, id);
            });
        }

        public async Task<string> GetVideoPathByFileName(string fileName)
        {
            return await Task.Run(() =>
            {
                var context = CrossCurrentActivity.Current.AppContext;
                string[] projection = { MediaStore.Video.Media.InterfaceConsts.Data, MediaStore.Video.Media.InterfaceConsts.DisplayName, MediaStore.Video.Media.InterfaceConsts.RelativePath };
                string selection = $"{MediaStore.Video.Media.InterfaceConsts.DisplayName} = ?";
                string[] selectionArgs = { fileName };

                using var cursor = context.ContentResolver.Query(MediaStore.Video.Media.ExternalContentUri, projection, selection, selectionArgs, null);
                if (cursor == null || !cursor.MoveToFirst()) return null;

                var path = cursor.GetString(cursor.GetColumnIndexOrThrow(MediaStore.Video.Media.InterfaceConsts.Data));
                // 复用上面的目录匹配逻辑即可
                return path;
            });
        }
    }
}

播放调用示例

拿到Uri后可以直接用于播放,比如调用系统播放器:

var videoUri = await DependencyService.Get<IVideoService>().GetVideoUriByFileName("你的视频文件名.mp4");
if (videoUri != null)
{
    var intent = new Intent(Intent.ActionView);
    intent.SetDataAndType(videoUri, "video/mp4");
    intent.AddFlags(ActivityFlags.GrantReadUriPermission);
    CrossCurrentActivity.Current.StartActivity(intent);
}

如果使用Xamarin.Forms的MediaElement控件,直接将Source属性设置为videoUri.ToString()即可播放。

优化建议

你可以在保存视频的时候就把生成的Uri或者路径持久化存储到本地(比如用Preferences),后续不需要每次查询MediaStore,效率更高也避免同名文件冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 18:18:02