如何通过Xamarin Forms便携类库结合依赖注入列出存储媒体文件?
在Xamarin.Forms便携类库中列出所有媒体文件的实现方案
嘿,我来帮你梳理下怎么搞定这个需求~你当前的代码是直接遍历Android根目录,但这里有几个明显的问题:一是根目录里几乎找不到你要的媒体文件(照片、视频这些都存在专门的媒体目录),二是Android 10及以上的Scoped Storage机制会限制你直接访问外部存储目录,三是既然用了便携类库,就得考虑跨平台的兼容性。结合你已经在用的依赖注入,我们可以这么来实现:
第一步:在便携类库中定义共享接口
首先要在PCL/共享项目里定义一个统一的接口,让各个平台去实现,这样上层代码不用关心具体平台的细节:
public interface IMediaFileService { // 获取所有类型的媒体文件 List<string> GetAllMediaFiles(); // 按类型筛选(图片/视频/音频) List<string> GetMediaFilesByType(MediaType mediaType); } // 枚举媒体类型,方便筛选 public enum MediaType { Image, Video, Audio }
第二步:Android平台的具体实现
因为你当前的代码是Android的,重点说这个平台。我们不用直接遍历目录,而是通过ContentResolver查询系统的媒体库——这是Android推荐的方式,既能避开Scoped Storage的限制,又能高效获取所有媒体文件。
先配置权限
在Android项目的AndroidManifest.xml里添加必要的权限(Android 13+可以用更细分的媒体权限):
<!-- Android 10及以下用这个 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Android 13+ 用更细分的权限,用户体验更好 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
实现接口
创建Android平台的服务类,标记依赖注入:
[assembly: Dependency(typeof(AndroidMediaFileService))] namespace YourAppName.Droid { public class AndroidMediaFileService : IMediaFileService { public List<string> GetAllMediaFiles() { var mediaFiles = new List<string>(); // 合并图片、视频、音频的结果 mediaFiles.AddRange(GetMediaFilesByType(MediaType.Image)); mediaFiles.AddRange(GetMediaFilesByType(MediaType.Video)); mediaFiles.AddRange(GetMediaFilesByType(MediaType.Audio)); return mediaFiles; } public List<string> GetMediaFilesByType(MediaType mediaType) { var files = new List<string>(); var contentResolver = Android.App.Application.Context.ContentResolver; // 定义要查询的字段:文件路径、文件名 string[] projection; Android.Net.Uri mediaUri; string sortOrder = $"{MediaStore.Images.Media.DateAdded} DESC"; // 按添加时间倒序 switch (mediaType) { case MediaType.Image: mediaUri = MediaStore.Images.Media.ExternalContentUri; projection = new[] { MediaStore.Images.Media.Data, MediaStore.Images.Media.DisplayName }; break; case MediaType.Video: mediaUri = MediaStore.Video.Media.ExternalContentUri; projection = new[] { MediaStore.Video.Media.Data, MediaStore.Video.Media.DisplayName }; break; case MediaType.Audio: mediaUri = MediaStore.Audio.Media.ExternalContentUri; projection = new[] { MediaStore.Audio.Media.Data, MediaStore.Audio.Media.DisplayName }; break; default: return files; } // 查询媒体库 using (var cursor = contentResolver.Query(mediaUri, projection, null, null, sortOrder)) { if (cursor != null && cursor.MoveToFirst()) { do { // 这里可以获取文件名或者文件路径,按需选择 var fileName = cursor.GetString(cursor.GetColumnIndex(projection[1])); // var filePath = cursor.GetString(cursor.GetColumnIndex(projection[0])); files.Add(fileName); } while (cursor.MoveToNext()); } } return files; } } }
第三步:iOS平台的适配(可选,跨平台需要)
如果你的应用要支持iOS,同样实现接口,iOS用Photos框架来获取媒体文件:
[assembly: Dependency(typeof(IosMediaFileService))] namespace YourAppName.iOS { public class IosMediaFileService : IMediaFileService { public List<string> GetAllMediaFiles() { var mediaFiles = new List<string>(); mediaFiles.AddRange(GetMediaFilesByType(MediaType.Image)); mediaFiles.AddRange(GetMediaFilesByType(MediaType.Video)); mediaFiles.AddRange(GetMediaFilesByType(MediaType.Audio)); return mediaFiles; } public List<string> GetMediaFilesByType(MediaType mediaType) { var files = new List<string>(); var fetchOptions = new PHFetchOptions(); PHFetchResult fetchResult = null; switch (mediaType) { case MediaType.Image: fetchResult = PHAsset.FetchAssets(PHAssetMediaType.Image, fetchOptions); break; case MediaType.Video: fetchResult = PHAsset.FetchAssets(PHAssetMediaType.Video, fetchOptions); break; case MediaType.Audio: fetchResult = PHAsset.FetchAssets(PHAssetMediaType.Audio, fetchOptions); break; } if (fetchResult != null) { foreach (PHAsset asset in fetchResult) { // 这里可以获取本地标识,或者进一步获取文件路径/文件名 files.Add(asset.LocalIdentifier); } } return files; } } }
记得在iOS项目的Info.plist里添加权限描述:
<key>NSPhotoLibraryUsageDescription</key> <string>需要访问您的照片库来获取媒体文件</string>
第四步:在便携类库中调用服务
现在回到你的PCL/共享项目,通过依赖注入获取服务并调用:
// 获取媒体服务实例 var mediaService = DependencyService.Get<IMediaFileService>(); // 获取所有媒体文件 var allMediaFiles = mediaService.GetAllMediaFiles(); // 只获取图片 var imageFiles = mediaService.GetMediaFilesByType(MediaType.Image);
几个重要的注意点
- 权限检查:在调用获取媒体的方法前,一定要先检查权限是否已授权。Android可以用Xamarin.Essentials的
Permissions.CheckStatusAsync<Permissions.StorageRead>(),iOS则检查PHPhotoLibrary.AuthorizationStatus。 - 性能优化:如果设备上媒体文件很多,别在主线程执行查询操作,放到后台线程(比如用
Task.Run),避免UI卡顿。 - Android Scoped Storage:直接遍历目录的方式在Android 10+会失效,用MediaStore是官方推荐的合规方案。
内容的提问来源于stack exchange,提问作者Srusti Thakkar
相关产品推荐
相关产品推荐

