swift-nio NIOFileSystem FileSystemProtocol API 指南:作用域文件句柄、文件管理操作与自定义文件系统扩展
发布时间:2026/9/25 1:46:45来源:尧图网络
后端网络【免费下载链接】swift-nioEvent-driven network application framework for high performance protocol servers clients, non-blocking.项目地址https://gitcode.com/gh_mirrors/sw/swift-nio点击查看免费下载本文基于 swift-nio 仓库中_NIOFileSystem模块的官方 DocC 扩展文档 FileSystemProtocol.md系统梳理FileSystemProtocol协议的六大 API 分组带作用域生命周期管理的文件打开withFileHandle系列、手动打开文件、文件信息查询、符号链接、文件管理复制/删除/移动/替换/建目录以及系统目录访问。读完后你可以直接使用FileSystem.shared完成异步非阻塞的本地文件操作也能理解每个选项OpenOptions、CopyStrategy、RemovalStrategy的默认值与底层映射甚至实现一个自己的文件系统后端。1. 协议定位所有文件 I/O 的统一抽象FileSystemProtocol定义在 FileSystemProtocol.swift是整个模块的核心接口。模块总览文档 index.md 指出_NIOFileSystem提供了具体的FileSystem用于操作本地文件系统同时通过一组协议允许你创建其他文件系统实现。协议本身是一个Sendable协议并声明了 4 个关联类型FileSystemProtocol.swift#L19-L34public protocol FileSystemProtocol: Sendable { /// Opens a file for reading - this handle type associatedtype ReadFileHandle: ReadableFileHandleProtocol /// for writing associatedtype WriteFileHandle: WritableFileHandleProtocol /// for reading and writing associatedtype ReadWriteFileHandle: ReadableAndWritableFileHandleProtocol /// for directories associatedtype DirectoryFileHandle: DirectoryFileHandleProtocol where DirectoryFileHandle.ReadFileHandle ReadFileHandle, DirectoryFileHandle.ReadWriteFileHandle ReadWriteFileHandle, DirectoryFileHandle.WriteFileHandle WriteFileHandle }关联类型之间的where约束保证了目录句柄打开子文件时返回的句柄类型与文件系统本身保持一致——这是实现自定义后端时最容易踩的坑。官方实现FileSystem是一个基于NIOThreadPool的结构体所有阻塞的系统调用都被提交到线程池执行避免阻塞 Swift 并发运行时FileSystem.swift#L30-L89let handle try await self.threadPool.runIfActive { let handle try self._openFile(forReadingAt: path, options: options).get() // 刚刚创建安全地转移到调用者所在的 Task return UnsafeTransfer(handle) }获取实例的三种方式FileSystem.shared全局共享实例默认 2 个工作线程可通过SWIFT_FILE_SYSTEM_THREAD_COUNT环境变量调整FileSystem(threadPool:)传入你自己的NIOThreadPool不由FileSystem负责关闭withFileSystem(numberOfThreads:) { fileSystem in ... }创建带独立线程池的实例闭包结束后自动shutdownGracefully()FileSystem.swift#L774-L788。错误模型方面模块抛出的顶层错误只有FileSystemError及 Swift 的CancellationErrorFileSystemError提供detailedDescription()输出结构化多行诊断信息FileSystemError.swift。官方给出的整体使用示例见 NIOFileSystemTour.swift 片段下文各节会拆解其中涉及的 API。2. 带生命周期管理的文件打开withFileHandle/withDirectoryHandle官方文档将这一组 API 归为 Opening files with managed lifecycles。它们封装了打开 → 执行 → 关闭的完整流程文件在execute闭包的生命周期内保持打开闭包返回前自动关闭无需调用者担心资源泄漏。实现位于 FileSystemProtocol.swift#L321-L432// 只读打开 func withFileHandleResult( forReadingAt path: FilePath, options: OpenOptions.Read OpenOptions.Read(), execute: (_ read: ReadFileHandle) async throws - Result ) async throws - Result // 只写打开默认新建文件不替换已有文件 func withFileHandleResult( forWritingAt path: FilePath, options: OpenOptions.Write .newFile(replaceExisting: false), execute: (_ write: WriteFileHandle) async throws - Result ) async throws - Result // 读写打开默认同上 func withFileHandleResult( forReadingAndWritingAt path: FilePath, options: OpenOptions.Write .newFile(replaceExisting: false), execute: (_ readWrite: ReadWriteFileHandle) async throws - Result ) async throws - Result // 打开目录 func withDirectoryHandleResult( atPath path: FilePath, options: OpenOptions.Directory OpenOptions.Directory(), execute: (_ directory: DirectoryFileHandle) async throws - Result ) async throws - Result几个值得注意的实现细节句柄禁止逃逸闭包。所有方法的文档都标注了 The handle passed toexecutemust not escape the closure——关闭由外层统一管理句柄一旦逃逸会导致重复关闭或使用已关闭描述符。失败路径上的关闭语义。写句柄的关闭使用withUncancellableTearDown区分成败成功时close()正常落地例如提交事务性创建失败时close(makeChangesVisible: false)丢弃未提交的变更FileSystemProtocol.swift#L366-L382。关闭是不可取消的withUncancellableTearDown即使调用方 Task 被取消close()也会执行完保证描述符不泄漏。官方示例中用它写入一个文件并顺带读目录NIOFileSystemTour.swift#L40-L60try await fileSystem.withFileHandle( forWritingAt: /Users/hal9000/demise-of-dave.txt, options: .newFile(replaceExisting: false) ) { file in let plan ByteBuffer(string: TODO...) try await file.write(contentsOf: plan.readableBytesView, toAbsoluteOffset: 0) } let path: FilePath? try await fileSystem.withDirectoryHandle(atPath: /Users/hal9000/Music) { directory in for try await entry in directory.listContents() { if entry.name daisy.mp3 { return entry.path // 提前 return 也会自动关闭句柄 } } return nil }3. 手动打开文件/目录openFile与openDirectory文档 Opening files 分组的四个方法是withFileHandle系列的底层原语区别在于关闭责任完全交给调用者适用于句柄需要跨越多个逻辑阶段、或需要在结构化闭包之外持有的场景func openFile(forReadingAt path: FilePath, options: OpenOptions.Read) async throws - ReadFileHandle func openFile(forWritingAt path: FilePath, options: OpenOptions.Write) async throws - WriteFileHandle func openFile(forReadingAndWritingAt path: FilePath, options: OpenOptions.Write) async throws - ReadWriteFileHandle func openDirectory(atPath path: FilePath, options: OpenOptions.Directory) async throws - DirectoryFileHandle协议约定FileSystemProtocol.swift#L38-L85打开的文件必须已存在否则抛出FileSystemError错误码为.notFound打开目录时目录必须已存在否则抛错创建目录应使用createDirectory(at:withIntermediateDirectories:permissions:)便捷重载openFile(forReadingAt:)、openDirectory(atPath:)不带 options等价于传入默认的OpenOptions.Read()/OpenOptions.Directory()FileSystemProtocol.swift#L434-L462。本地实现FileSystem的这四个方法都走open(2)系统调用先在NIOThreadPool中同步执行再把句柄以UnsafeTransfer移交回调用方 TaskFileSystem.swift#L91-L200。3.1OpenOptions参数详解三类 options 都位于 OpenOptions.swift#L18-L151公共字段字段类型默认值含义followSymbolicLinksBooltrue末段路径是符号链接时是否跟随为false且遇到符号链接则抛错closeOnExecBoolfalse将描述符标记为 close-on-execO_CLOEXECOpenOptions.Write额外包含两个字段existingFile: OpenOptions.ExistingFile对已存在文件的处理策略取值.none存在即报错O_EXCL、.open直接打开、.truncate截断等价O_TRUNCnewFile: OpenOptions.NewFile?是否允许创建nil表示不创建NewFile含permissionsnil时用默认权限和transactionalCreation默认true新建文件在close()且无异常时才真正落盘仅在existingFile .none时生效。两个最常用工厂方法OpenOptions.swift#L112-L149// 新建文件replaceExistingtrue 时替换同名文件truncate static func newFile(replaceExisting: Bool, permissions: FilePermissions? nil) - Self // 修改已有文件createIfNecessarytrue 时不存在则创建 static func modifyFile(createIfNecessary: Bool, permissions: FilePermissions? nil) - Self底层映射关系可从descriptorOptions属性看到OpenOptions.swift#L209-L234followSymbolicLinks false → .noFollow、closeOnExec true → .closeOnExec、newFile ! nil → .create、existingFile .none → .exclusiveCreate、.truncate → .truncate。目录 options 还会额外带上.directory标志。默认权限常量OpenOptions.swift#L283-L298常规文件defaultsForRegularFile属主读写rw-、组/其他只读r--目录defaultsForDirectory属主读写执行rwx、组/其他读执行r-x。4. 文件信息info(forFileAt:infoAboutSymbolicLink:)func info(forFileAt path: FilePath, infoAboutSymbolicLink: Bool) async throws - FileInfo?返回路径处的文件信息文件不存在时返回nil而不是抛错——因此它是文件是否存在的标准检查手段。infoAboutSymbolicLink为true时返回链接本身的信息为false时返回链接目标的信息FileSystemProtocol.swift#L136-L147。便捷重载info(forFileAt:)固定传入falseFileSystemProtocol.swift#L464-L473。FileInfo中typeFileType、permissions、时间戳等字段在平台间存在差异模块文档 index.md 明确说明不同平台的FileInfo表示不同需查阅FileInfo本身的文档。示例用法NIOFileSystemTour.swift#L15-L19if let info try await fileSystem.info(forFileAt: /Users/hal9000/demise-of-dave.txt) { print(demise-of-dave.txt has type \(info.type)) } else { print(demise-of-dave.txt doesnt exist) }5. 符号链接协议提供两个符号链接操作FileSystemProtocol.swift#L151-L169// 在 path 处创建指向 destinationPath 的符号链接path 处已有文件/目录则抛错 func createSymbolicLink(at path: FilePath, withDestination destinationPath: FilePath) async throws // 读取符号链接的目标路径 func destinationOfSymbolicLink(at path: FilePath) async throws - FilePath官方 Tour 示例NIOFileSystemTour.swift#L76-L81try await fileSystem.createSymbolicLink(at: /Users/hal9000/Backup, withDestination: /Volumes/Tardis) // 打开符号链接默认就打开其目标多数场景无需读取 destination try await fileSystem.withDirectoryHandle(atPath: /Users/hal9000/Backup) { directory in ... }注意打开行为与OpenOptions中followSymbolicLinks的联动默认跟随链接置为false时末段组件若是符号链接会抛错可用destinationOfSymbolicLink(at:)显式解析目标。6. 文件管理复制、删除、移动、替换、建目录这是文档 Managing files 分组也是协议中最复杂的部分完整实现语义都写在协议方法的文档注释中。6.1 复制copyItem全家族完整签名FileSystemProtocol.swift#L236-L251func copyItem( at sourcePath: FilePath, to destinationPath: FilePath, strategy copyStrategy: CopyStrategy, replaceExisting: Bool, shouldProceedAfterError: escaping Sendable (_ source: DirectoryEntry, _ error: Error) async throws - Void, shouldCopyItem: escaping Sendable (_ source: DirectoryEntry, _ destination: FilePath) async - Bool ) async throws语义要点均来自 FileSystemProtocol.swift#L173-L235 的文档注释可能抛出的错误码sourcePath不存在 →.notFoundreplaceExisting false且destinationPath已存在、或其父目录不存在 →.invalidArgument其他错误也可能发生若sourcePath是符号链接只复制链接本身复制结果保留权限与扩展属性在文件系统支持时错误回调契约实现方在抛错前必须先调用shouldProceedAfterError若闭包正常返回视为继续该错误被吞掉闭包抛错则copyItem抛错并停止复制。实现方 MUST 对每个出错项恰好调用一次、且不得持锁MAY 并发多次调用sequential策略除外。抛出错误后destinationPath内的状态是未定义的实现方无义务清理过滤回调契约shouldCopyItemMUST 在每个项含sourcePath本身被复制前恰好调用一次、不得持锁、且必须先于父目录检查子项——父目录被过滤则其内部所有项都不再检查sequential策略下同一时刻只会有一个回调在执行。CopyStrategy定义了目录级复制的并发度IOStrategy.swift#L58-L103工厂含义.platformDefault平台合理默认假设同一时刻只有一次复制、且复制不是设备主要活动.sequential异步执行但一次只有一项操作保证shouldCopyItem回调串行.parallel(maxDescriptors:)限制复制中并发打开的描述符数量必须 ≥ 2否则抛.invalidArgument便捷重载都在扩展里FileSystemProtocol.swift#L475-L645// 最简平台默认策略出错即中止全部项都复制 try await fileSystem.copyItem(at: /Users/hal9000/Music, to: /Volumes/Tardis/Music) // 带回调策略默认 .platformDefaultreplaceExisting 固定 false try await fileSystem.copyItem(at: src, to: dst, shouldProceedAfterError: { ... }, shouldCopyItem: { ... })另有一个标记available(*, deprecated)的旧重载copyItem(at:to:shouldProceedAfterError:shouldCopyFile:)其shouldCopyFile闭包参数是(FilePath, FilePath)官方提示迁移为接收DirectoryEntry的新版本实现内部用.sequential策略保持旧版串行回调语义。6.2 删除removeItem(at:strategy:recursively:)discardableResult func removeItem(at path: FilePath, strategy removalStrategy: RemovalStrategy, recursively removeItemRecursively: Bool) async throws - IntFileSystemProtocol.swift#L253-L277目标必须是常规文件、符号链接或目录路径不存在时返回 0 而非抛错返回值是被删除的项数recursively true等价rm -rfalse等价rmdir对非目录无效符号链接只删链接不删目标removalStrategy与复制策略同族IOStrategy.swift#L125-L168.platformDefault/.sequential/.parallel(maxDescriptors:)区别是删除最少只需1个描述符只扫描目录时占用maxDescriptors 1才抛.invalidArgument便捷重载FileSystemProtocol.swift#L647-L716removeItem(at:)等价于.platformDefault recursively: trueremoveItem(at:recursively:)与removeItem(at:strategy:)分别补全其余默认参数。目录删除的并行扫描实现在 ParallelRemoval.swift。6.3 移动与替换moveItem/replaceItemfunc moveItem(at sourcePath: FilePath, to destinationPath: FilePath) async throws func replaceItem(at destinationPath: FilePath, withItemAt existingPath: FilePath) async throwsmoveItemsourcePath不存在 →.notFounddestinationPath已存在或父目录不存在 →.invalidArgument源是符号链接时只移动链接FileSystemProtocol.swift#L279-L293replaceItem行为与moveItem相同但允许替换已存在的destinationPath——替换完成后existingPath被移除因此原路径不再存在。destinationPath不必存在且允许文件与目录互相替换若复制到destinationPath成功但从existingPath移除失败错误码为.ioFileSystemProtocol.swift#L295-L316。6.4 创建目录createDirectoryfunc createDirectory(at path: FilePath, withIntermediateDirectories createIntermediateDirectories: Bool, permissions: FilePermissions?) async throws对应mkdir(2)path处已有目录或文件则抛错createIntermediateDirectories false时path的完整前缀必须已存在为true时自动创建全部中间目录便捷重载createDirectory(at:withIntermediateDirectories:)固定使用permissions: .defaultsForDirectoryrwx/r-x/r-xFileSystemProtocol.swift#L718-L741。7. 系统目录当前目录、临时目录与作用域临时目录文档 System directories 分组列出三个成员FileSystemProtocol.swift#L106-L132var currentWorkingDirectory: FilePath { get async throws } // 当前工作目录 var temporaryDirectory: FilePath { get async throws } // 系统临时目录协议中还另有 homeDirectory以及核心的作用域 APIwithTemporaryDirectoryFileSystemProtocol.swift#L743-L777func withTemporaryDirectoryResult( prefix: FilePath? nil, options: OpenOptions.Directory OpenOptions.Directory(), execute: (_ directory: DirectoryFileHandle, _ path: FilePath) async throws - Result ) async throws - Result实现要点prefix为nil时以temporaryDirectory为前缀在模板尾部追加 8 个XXXXXXXXX调用createTemporaryDirectory(template:)由系统替换为唯一组合生成真实目录名——协议要求模板至少以 3 个X结尾且模板中的中间目录若不存在会被创建通过withUncancellableTearDown保证无论execute成功还是抛错退出时都会以.platformDefault策略递归删除整个临时目录。这是写测试数据沙箱下载暂存区这类逻辑的推荐模式无需defer或 finally 清理。8. 实现一个自定义文件系统模块文档 index.md 的 Creating a File System 一节指出实现FileSystemProtocol即可创建自定义文件系统它依赖以下句柄协议族FileSystemProtocol ├── FileHandleProtocol // 所有句柄的基础 ├── ReadableFileHandleProtocol // ReadFileHandle 关联类型 ├── WritableFileHandleProtocol // WriteFileHandle 关联类型 ├── ReadableAndWritableFileHandleProtocol // ReadWriteFileHandle 关联类型 └── DirectoryFileHandleProtocol // DirectoryFileHandle 关联类型这些协议分别定义在 FileHandleProtocol.swift、ReadableFileHandleProtocol.md 等对应文档中。实现时的关键点协议要求的必须实现方法只有第二节列出的四个openFile/openDirectory加上createDirectory、三个目录属性、createTemporaryDirectory、info、两个符号链接方法和四个文件管理方法withFileHandle系列、各类便捷openFile/copyItem/removeItem重载都是基于必须实现方法的默认扩展自动获得关联类型where约束DirectoryFileHandle.ReadFileHandle ReadFileHandle等要求目录句柄打开子文件时返回你定义的句柄类型copyItem的错误/过滤回调契约每次恰好一次、不持锁、父先于子对实现方是强制语义目录级并行扫描可参考 ParallelDirCopy.swift 的实现方式由于协议是Sendable的你的文件系统类型及句柄类型都应满足并发安全要求。9. 行为验证测试用例指引若需验证以上语义可参考仓库中的测试FileSystemTests.swift复制/移动/替换/删除、临时目录等协议级行为的集成验证FileHandleTests.swift句柄读写字节偏移、事务性创建等DirectoryEntriesTests.swift目录列举listContents()返回的DirectoryEntry序列FileSystemTestsSPI.swift通过_spi(Testing)入口验证OpenOptions默认权限等内部约定。10. 小结API 速查表分组对应文档 TopicsAPI生命周期责任关键默认值管理式打开withFileHandle(forReadingAt:options:execute:)框架自动关闭OpenOptions.Read()管理式打开withFileHandle(forWritingAt:options:execute:)框架自动关闭失败时makeChangesVisible: false.newFile(replaceExisting: false)管理式打开withFileHandle(forReadingAndWritingAt:options:execute:)框架自动关闭.newFile(replaceExisting: false)管理式打开withDirectoryHandle(atPath:options:execute:)框架自动关闭OpenOptions.Directory()手动打开openFile(forReadingAt:)/openFile(forWritingAt:options:)/openFile(forReadingAndWritingAt:options:)/openDirectory(atPath:options:)调用者必须关闭各OpenOptions文件信息info(forFileAt:infoAboutSymbolicLink:)—不存在返回nil符号链接createSymbolicLink(at:withDestination:)/destinationOfSymbolicLink(at:)——文件管理copyItem4 个重载—最简版.platformDefault、replaceExisting: false、出错即停文件管理removeItem(at:)/(at:recursively:)/(at:strategy:)/(at:strategy:recursively:)—.platformDefault 递归不存在返回 0文件管理moveItem(at:to:)/replaceItem(at:withItemAt:)—替换版允许目标已存在文件管理createDirectory(at:withIntermediateDirectories:permissions:)—便捷版用目录默认权限系统目录currentWorkingDirectory/temporaryDirectory——系统目录withTemporaryDirectory(prefix:options:execute:)自动创建递归删除前缀默认temporaryDirectory模板 8 个X本地文件系统的具体实现FileSystem 线程池 open(2)等系统调用、跨平台差异Apple 平台复制走 clone、扩展属性可能在部分系统不可用、路径格式差异等见 index.md 与 FileSystem.swift可作为深入阅读的入口。赞分享后端网络【免费下载链接】swift-nioEvent-driven network application framework for high performance protocol servers clients, non-blocking.项目地址https://gitcode.com/gh_mirrors/sw/swift-nio点击查看免费下载相关推荐Shiro文件系统文件操作与管理指南Shiro文件系统文件操作与管理指南 Shiro作为一个极简主义的个人网站主题虽然在设计上追求简洁但其文件系统架构却十分完善。本文将为您详细介绍Shiro前端快速上手Goose AI Agent从一句自然语言到自动化部署的完整路径快速上手Goose AI Agent从一句自然语言到自动化部署的完整路径 GooseGoose AI Agent是一个开源的本地AI代理它跑在你自己的机人工智能大模型AI AgentAI 应用本地部署MCP ClientsMCP 服务工具调用桌面应用CLI掌握Carbon语言文件系统高效路径处理与文件操作全指南掌握Carbon语言文件系统高效路径处理与文件操作全指南 Carbon语言作为一种实验性的系统级编程语言其文件系统API设计融合了现代安全性与跨平台兼容性。编程语言编译器标准库上一篇家庭云游戏部署指南使用Sunshine打造低延迟远程游戏体验下一篇终极MASA模组汉化包一键让Minecraft模组界面全中文创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网