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
相关产品推荐
相关产品推荐

