mcpField
M
maui-media-picker

maui-media-picker

github.com/davidortinau开发17647 次安装16 周前更新
代码仓库

关于 maui-media-picker

面向 .NET MAUI 开发者的 Media Picker 使用指南,涵盖媒体选择与拍摄的常见陷阱及平台差异,帮助安全处理结果、权限和流。

主要功能

  1. 01处理取消和空结果
  2. 02在 UI 线程调用选择器
  3. 03检查相机拍摄支持情况
  4. 04释放打开的流
  5. 05处理平台权限与选择数量限制

适用场景

  1. 01添加单张或多张照片选择
  2. 02在检查相机能力后拍摄照片
  3. 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