关于 maui-media-picker
面向 .NET MAUI 开发者的 Media Picker 使用指南,涵盖媒体选择与拍摄的常见陷阱及平台差异,帮助安全处理结果、权限和流。
主要功能
- 01处理取消和空结果
- 02在 UI 线程调用选择器
- 03检查相机拍摄支持情况
- 04释放打开的流
- 05处理平台权限与选择数量限制
适用场景
- 01添加单张或多张照片选择
- 02在检查相机能力后拍摄照片
- 03配置 Android 和 iOS 媒体权限
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ davidortinau/ maui-skills --skill maui-media-picker安装后在 Claude Code 中输入/maui-media-picker确认已加载。
附带文件references/media-picker-api.md
详细介绍
.NET MAUI Media Picker — 注意事项与最佳实践
常见错误
1. 忘记检查是否取消操作
单选会返回 null,多选会返回空列表。不进行检查会导致 NullReferenceException。
// ❌ Crashes when user cancels
var photo = await MediaPicker.Default.PickPhotoAsync();
using var stream = await photo.OpenReadAsync();
// ✅ Always check for null / empty
var photo = await MediaPicker.Default.PickPhotoAsync();
if (photo is null) return;
using var stream = await photo.OpenReadAsync();
2. 从后台线程调用
所有 MediaPicker 方法都必须在 UI 线程上运行,否则会抛出异常。
// ❌ Will throw on some platforms
await Task.Run(async () => await MediaPicker.Default.PickPhotoAsync());
// ✅ Call directly from a command or event handler on the UI thread
var photo = await MediaPicker.Default.PickPhotoAsync();
3. 不检查 IsCaptureSupported
没有摄像头的设备(模拟器、某些平板电脑)在调用拍摄方法时会抛出异常。
// ❌ Crashes on devices without a camera
var photo = await MediaPicker.Default.CapturePhotoAsync();
// ✅ Guard with capability check
if (MediaPicker.Default.IsCaptureSupported)
{
var photo = await MediaPicker.Default.CapturePhotoAsync();
}
4. 不释放流
OpenReadAsync() 返回的流必须释放。
// ❌ Leaks the stream
var stream = await photo.OpenReadAsync();
var bytes = ReadAllBytes(stream);
// ✅ Dispose with using
using var stream = await photo.OpenReadAsync();
平台注意事项
⚠️ Android/Windows 上不会强制执行 SelectionLimit
MediaPickerOptions.SelectionLimit 仅供参考。请务必自行验证数量:
var options = new MediaPickerOptions { SelectionLimit = 5 };
var results = await MediaPicker.Default.PickPhotosAsync(options);
if (results.Count() > 5)
{
// Warn user or take only the first 5
results = results.Take(5);
}
⚠️ Android 存储权限在 API 33 处有变化
- API ≤ 32:需要
READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE - API ≥ 33:需要
READ_MEDIA_IMAGES/READ_MEDIA_VIDEO(宽泛的存储权限会被忽略) - 在旧权限上使用
android:maxSdkVersion="32",以避免 Play Store 警告
⚠️ iOS 要求提供全部四个 plist 键才能完整使用功能
缺少 NSCameraUsageDescription、NSMicrophoneUsageDescription、
NSPhotoLibraryUsageDescription 或 NSPhotoLibraryAddUsageDescription 中的任何一个,
都会在访问相应功能时导致运行时崩溃。
⚠️ Android 的相机意图需要 <queries>
如果 AndroidManifest.xml 中没有 IMAGE_CAPTURE 查询,由于包可见性限制,
CapturePhotoAsync 在 Android 11+ 上可能会静默失败。
选择指南
| 场景 | 方法 |
|---|---|
| 用户选择一张照片 | PickPhotoAsync() |
| 用户选择多张照片(.NET 10+) | 使用 SelectionLimit 的 PickPhotosAsync() |
| 应用需要拍摄照片 | 检查 IsCaptureSupported → CapturePhotoAsync() |
| 永久保存所选文件 | 将流复制到 FileSystem.AppDataDirectory |
| 需要照片元数据 | 在 MediaPickerOptions 中设置 PreserveMetaData = true |
检查清单
- 所有选择器调用都在 UI 线程上运行
- 每次选择或拍摄后都检查是否为 null/空
- 调用拍摄方法前检查
IsCaptureSupported - 使用
using释放流 - Android 清单包含带有
maxSdkVersion的新旧存储权限 - Android 清单包含用于相机意图的
<queries>块 - iOS
Info.plist包含全部四个用途说明键 - 在代码中验证
SelectionLimit,而不是信任平台
评论
支持 Markdown