mcpField
M
maui-file-handling

maui-file-handling

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

关于 maui-file-handling

.NET MAUI 文件处理技能,为开发者提供跨平台文件读写与选择器配置的实践指导。重点说明平台差异和常见陷阱,帮助避免路径、权限及流处理错误。

主要功能

  1. 01使用 OpenReadAsync 读取所选文件
  2. 02按平台配置文件类型格式
  3. 03将捆绑文件复制后再修改
  4. 04使用跨平台目录处理路径
  5. 05检查选择器取消及 Android 权限

适用场景

  1. 01实现跨平台文件选择与读取
  2. 02配置 Android、iOS、macOS 和 Windows 文件筛选
  3. 03保存并修改应用捆绑资源
  4. 04排查 Android 流长度及权限问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/davidortinau/maui-skills --skill maui-file-handling

安装后在 Claude Code 中输入/maui-file-handling确认已加载。

附带文件references/file-handling-api.md

详细介绍

.NET MAUI 文件处理

重要:始终使用 OpenReadAsync(),不要使用 FullPath

某些平台(尤其是 Android)返回的是内容 URI,而非文件系统 路径。直接读取 FullPath 会引发异常或返回空数据。

// ❌ Breaks on Android — FullPath may be a content:// URI
var result = await FilePicker.Default.PickAsync();
var bytes = File.ReadAllBytes(result.FullPath);

// ✅ Works on all platforms
var result = await FilePicker.Default.PickAsync();
if (result is not null)
{
    using var stream = await result.OpenReadAsync();
    // process stream
}

平台文件类型格式注意事项

各个平台为自定义 FilePickerFileType 使用的格式各不相同。 格式混用会导致文件选择器不显示任何文件或崩溃。

平台格式示例
AndroidMIME 类型"application/json"
iOS / macOSUTType 标识符"public.json"
Windows以点开头的扩展名".json"
// ❌ Using file extensions for Android — picker shows nothing
{ DevicePlatform.Android, new[] { ".json", ".txt" } }

// ✅ Correct MIME types for Android
{ DevicePlatform.Android, new[] { "application/json", "text/plain" } }

常见问题

捆绑文件为只读

运行时无法修改 Resources/Raw 资源。尝试写入会引发异常(或在某些平台上静默失败)。

// ❌ Trying to write to a bundled file
var path = "data.json"; // inside Resources/Raw
File.WriteAllText(path, newContent); // fails

// ✅ Copy to AppDataDirectory first, then modify
string targetPath = Path.Combine(FileSystem.Current.AppDataDirectory, "data.json");
if (!File.Exists(targetPath))
{
    using var source = await FileSystem.Current.OpenAppPackageFileAsync("data.json");
    using var dest = File.Create(targetPath);
    await source.CopyToAsync(dest);
}
// Now safe to read/write targetPath
捆绑文件的子目录会被展平

在某些平台上,Resources/Raw/subdir/file.txt 会变成仅有 file.txt。 无论子目录结构如何,都应使用唯一文件名。

iOS 沙盒路径会在重新构建时变化

iOS 沙盒路径中包含一个应用 GUID,该 GUID 会在每次干净构建后变化。 硬编码的绝对路径会悄然失效。

// ❌ Hard-coded path — breaks after clean rebuild
var path = "/var/mobile/.../Documents/data.json";

// ✅ Always use the FileSystem helper
var path = Path.Combine(FileSystem.Current.AppDataDirectory, "data.json");
Android:捆绑流没有 Length

OpenAppPackageFileAsync 可能会返回一个调用 .Length 时引发 NotSupportedException 的流。如果需要获取大小,请复制到 MemoryStream。

// ❌ Throws on Android
using var stream = await FileSystem.Current.OpenAppPackageFileAsync("data.json");
var size = stream.Length; // NotSupportedException

// ✅ Copy first if you need the length
using var stream = await FileSystem.Current.OpenAppPackageFileAsync("data.json");
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
var size = ms.Length;
Windows:虚拟化文件系统

打包应用会将写入操作静默重定向到 %AppData% 等传统路径。 为确保跨平台路径可靠,请始终使用 AppDataDirectory 和 CacheDirectory。

FilePicker 在取消时会返回 null
// ❌ NullReferenceException when user cancels
var result = await FilePicker.Default.PickAsync();
using var stream = await result.OpenReadAsync();

// ✅ Always null-check
var result = await FilePicker.Default.PickAsync();
if (result is null) return;
using var stream = await result.OpenReadAsync();

Android 权限(API 33+ 变更)

Android 13 使用细分的媒体权限取代了 READ_EXTERNAL_STORAGE。 在 API 33+ 上使用旧权限不会授予任何权限,且不会提示。

Android 版本所需权限
≤ 12 (API 32)READ_EXTERNAL_STORAGE
≥ 13 (API 33)READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO

检查清单

  • 使用 OpenReadAsync()——绝不直接读取 FullPath
  • 访问属性前,先检查 FilePicker 结果是否为 null
  • 自定义 FilePickerFileType 使用对应平台的正确格式(MIME / UTType / 扩展名)
  • 修改捆绑文件前,先将其复制到 AppDataDirectory
  • Resources/Raw 中使用唯一文件名(子目录会被展平)
  • Android 清单声明与目标 API 级别对应的正确权限
  • macOS:App Sandbox 权限包含文件访问权限

评论

支持 Markdown