diff --git a/website/docs/doc/GeneralUpdate.Bowl.md b/website/docs/doc/GeneralUpdate.Bowl.md index b15cfd1..d490a12 100644 --- a/website/docs/doc/GeneralUpdate.Bowl.md +++ b/website/docs/doc/GeneralUpdate.Bowl.md @@ -417,4 +417,4 @@ MyApp/ - [Bowl 示例代码](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/Bowl) - [GeneralUpdate 仓库](https://github.com/GeneralLibrary/GeneralUpdate) -- [Dump 指南](../guide/Dump.md) +- [Dump 指南](../guide/Dump) diff --git a/website/docs/doc/GeneralUpdate.Core.md b/website/docs/doc/GeneralUpdate.Core.md index bf08797..ac3ea6c 100644 --- a/website/docs/doc/GeneralUpdate.Core.md +++ b/website/docs/doc/GeneralUpdate.Core.md @@ -143,7 +143,7 @@ sidebar_position: 5 | `Cancel()` | 无 | `void` | UI 中提供"取消更新"按钮 | 触发内部 `CancellationTokenSource` | | `SetConfig(UpdateRequest)` | `configInfo` — 更新配置对象 | `GeneralUpdateBootstrap` | 主程序内显式配置更新参数 | 会调用 `Validate()` 检查关键字段 | | `SetConfig(string)` | `filePath` — JSON 配置文件路径 | `GeneralUpdateBootstrap` | 从文件读取更新配置 | 支持相对路径和绝对路径;UTF-8 JSON 格式 | -| `SetSource(...)` | `updateUrl`, `appSecretKey`, `reportUrl?`, `scheme?`, `token?` | `GeneralUpdateBootstrap` | 轻配置入口,配合 manifest 使用 | 只提供服务端入口和密钥,身份信息由 manifest 补齐 | +| `SetSource(...)` | `updateUrl`, `appSecretKey`, `reportUrl?`, `scheme?`, `token?`, `authScheme?`, `basicUsername?`, `basicPassword?`, `installPath?` | `GeneralUpdateBootstrap` | 轻配置入口,配合 manifest 使用 | 只提供服务端入口和密钥,身份信息由 manifest 补齐;支持 HMAC / Bearer / Basic 多种认证方式 | | `SetOption(Option, T)` | `option` — 选项键, `value` — 选项值 | `GeneralUpdateBootstrap` | 设置运行时选项 | 传入 `null` 给可空选项会移除当前设置 | | `UseDiffPipeline(Action)` | `configure` — 差分管道配置委托 | `GeneralUpdateBootstrap` | 替换或调整差分补丁管道 | 未调用时使用默认配置 | | `AddListenerUpdateInfo(...)` | `EventHandler` | `GeneralUpdateBootstrap` | 接收服务端版本信息 | 无更新时也会触发(`Info.Code = 404`) | @@ -197,6 +197,9 @@ sidebar_position: 5 | `SetDriverDirectory(string)` | `path` | `UpdateRequestBuilder` | 设置驱动目录 | — | | `SetScheme(string)` | `scheme` | `UpdateRequestBuilder` | 设置认证方案 | — | | `SetToken(string)` | `token` | `UpdateRequestBuilder` | 设置认证令牌 | — | +| `SetAuthScheme(AuthScheme)` | `authScheme` — 认证方案枚举 | `UpdateRequestBuilder` | 设置认证方案(Hmac / Bearer / ApiKey / Basic) | 默认 `Hmac` | +| `SetBasicUsername(string)` | `username` | `UpdateRequestBuilder` | 设置 Basic 认证用户名 | 需配合 `SetAuthScheme(AuthScheme.Basic)` | +| `SetBasicPassword(string)` | `password` | `UpdateRequestBuilder` | 设置 Basic 认证密码 | 需配合 `SetAuthScheme(AuthScheme.Basic)` | | `SetFiles(List)` | `files` | `UpdateRequestBuilder` | 设置跳过文件列表 | — | | `SetFormats(List)` | `formats` | `UpdateRequestBuilder` | 设置跳过扩展名列表 | — | | `SetDirectories(List)` | `dirs` | `UpdateRequestBuilder` | 设置跳过目录列表 | — | @@ -1007,5 +1010,5 @@ var failedVersion = Environments.Get("UpgradeFail"); - [GeneralUpdate 仓库](https://github.com/GeneralLibrary/GeneralUpdate) - [Samples 示例代码](https://github.com/GeneralLibrary/GeneralUpdate-Samples) - [GeneralUpdate.Tools](https://github.com/GeneralLibrary/GeneralUpdate.Tools) -- [入门实战手册](../quickstart/Beginner%20cookbook.md) -- [组件文档](./GeneralUpdate.Core.md) +- [入门实战手册](../quickstart/Beginner%20cookbook) +- [组件文档](./GeneralUpdate.Core) diff --git a/website/docs/doc/GeneralUpdate.Differential.md b/website/docs/doc/GeneralUpdate.Differential.md index df623d3..7613c1f 100644 --- a/website/docs/doc/GeneralUpdate.Differential.md +++ b/website/docs/doc/GeneralUpdate.Differential.md @@ -10,7 +10,7 @@ sidebar_position: 6 ### 1.1 组件概述 -**GeneralUpdate.Differential** 是 GeneralUpdate 的二进制差分组件,专注解决"一个旧文件 + 一个补丁文件 = 一个新文件"的问题。它提供可替换的文件级差分算法(BSDIFF 4.0 / Streaming HDiff)、补丁压缩抽象(BZip2 / Deflate / Brotli 预留)和 BSDIFF 兼容补丁读写能力。 +**GeneralUpdate.Differential** 是 GeneralUpdate 的二进制差分组件,专注解决"一个旧文件 + 一个补丁文件 = 一个新文件"的问题。它提供可替换的文件级差分算法(BSDIFF 4.0 / Streaming HDiff)、补丁压缩抽象(BZip2 / Deflate,源码中预留 .NET 6+ Brotli)和 BSDIFF 兼容补丁读写能力。 目录级对比、批量补丁生成、并行调度、删除文件处理和更新流程编排由 `GeneralUpdate.Core` 的 `DiffPipeline` 或 `GeneralUpdate.Tools` 承担。 @@ -21,7 +21,7 @@ sidebar_position: 6 | 文件级差分生成 | `CleanAsync(oldFile, newFile, patchFile)` — 对比新旧文件生成 `.patch` 补丁 | | 文件级差分应用 | `DirtyAsync(oldFile, newFile, patchFile)` — 旧文件 + 补丁 → 新文件 | | 可替换差分算法 | `BsdiffDiffer`(BSDIFF 4.0,后缀排序)和 `StreamingHdiffDiffer`(块哈希索引) | -| 可替换压缩格式 | BZip2 (0x00)、Deflate (0x01),源码中预留 .NET 6+ Brotli (0x02) | +| 可替换压缩格式 | BZip2 (0x00)、Deflate (0x01),源码中通过 `#if NET6_0_OR_GREATER` 条件编译预留 Brotli (0x02) | | BSDIFF 兼容格式 | 写入 33 字节扩展头(32 字节 BSDIFF40 + 1 字节压缩格式),兼容 32 字节旧头 | | 线程安全 | 内置 differ 和压缩提供器均支持并发调用 | @@ -93,7 +93,7 @@ Differential 本身是底层库,不提供配置类。所有参数通过构造 | --- | --- | --- | --- | | `BZip2CompressionProvider` | `0x00` | 完全可用 | BSDIFF 旧补丁兼容,解压成本较高 | | `DeflateCompressionProvider` | `0x01` | 完全可用 | 解压速度更友好,适合客户端批量应用 | -| `BrotliCompressionProvider` | `0x02` | 仅 .NET 6+ 编译(源码预留) | 生产不建议使用 | +| `BrotliCompressionProvider` | `0x02` | 仅 .NET 6+ 编译(源码中为完整实现,通过 `#if NET6_0_OR_GREATER` 条件编译) | 当前 `netstandard2.0` 包中不包含,生产不建议使用 | ### 3.2 实例方法 @@ -363,5 +363,5 @@ await new GeneralUpdateBootstrap() - [GeneralUpdate 仓库](https://github.com/GeneralLibrary/GeneralUpdate) - [Samples 差分示例](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/Hub/Samples/DifferentialSample.cs) -- [Core DiffPipeline 文档](GeneralUpdate.Core.md) -- [Tools 打包指南](../guide/Packaging.md) +- [Core DiffPipeline 文档](GeneralUpdate.Core) +- [Tools 打包指南](../guide/Packaging) diff --git a/website/docs/doc/GeneralUpdate.Drivelution.md b/website/docs/doc/GeneralUpdate.Drivelution.md index 12b82aa..bf93c9f 100644 --- a/website/docs/doc/GeneralUpdate.Drivelution.md +++ b/website/docs/doc/GeneralUpdate.Drivelution.md @@ -554,4 +554,4 @@ GeneralTracer.SetTracingEnabled(true); - [驱动更新示例](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/Hub/Samples/ImDiskQuickInstallSample.cs) - [GeneralUpdate 仓库](https://github.com/GeneralLibrary/GeneralUpdate) -- [GeneralUpdate.Drivelution 组件文档](../doc/GeneralUpdate.Drivelution.md) +- [GeneralUpdate.Drivelution 组件文档](../doc/GeneralUpdate.Drivelution) diff --git a/website/docs/doc/GeneralUpdate.Extension.md b/website/docs/doc/GeneralUpdate.Extension.md index 569f5d7..81e048a 100644 --- a/website/docs/doc/GeneralUpdate.Extension.md +++ b/website/docs/doc/GeneralUpdate.Extension.md @@ -676,4 +676,4 @@ report-extension_1.0.0.zip - [扩展管理示例](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/Hub/Samples/ExtensionSample.cs) - [GeneralUpdate 仓库](https://github.com/GeneralLibrary/GeneralUpdate) -- [打包指南](../guide/Packaging.md) +- [打包指南](../guide/Packaging) diff --git a/website/docs/doc/UpgradeHub.md b/website/docs/doc/UpgradeHub.md index feb255a..f61a8bb 100644 --- a/website/docs/doc/UpgradeHub.md +++ b/website/docs/doc/UpgradeHub.md @@ -363,4 +363,4 @@ await new GeneralClientBootstrap() - **示例代码:** [查看 GitHub 示例](https://github.com/GeneralLibrary/GeneralUpdate-Samples/blob/main/src/Push/Program.cs) - **主仓库:** [GeneralUpdate 项目](https://github.com/GeneralLibrary/GeneralUpdate) -- **相关组件:** [GeneralUpdate.Core](./GeneralUpdate.Core.md) | [GeneralUpdate.Core](./GeneralUpdate.Core.md) +- **相关组件:** [GeneralUpdate.Core](./GeneralUpdate.Core) | [GeneralUpdate.Core](./GeneralUpdate.Core) diff --git a/website/docs/guide/FAQ.md b/website/docs/guide/FAQ.md index 096cdf3..3e60d12 100644 --- a/website/docs/guide/FAQ.md +++ b/website/docs/guide/FAQ.md @@ -332,7 +332,7 @@ await new GeneralClientBootstrap() ## 相关资源 -- **[GeneralUpdate.Core](../doc/GeneralUpdate.Core.md)** — 核心更新引擎 -- **[入门实战手册](../quickstart/Beginner%20cookbook.md)** — 从零跑通更新闭环 -- **[GeneralUpdate.Tools](../quickstart/GeneralUpdate.PacketTool.md)** — 打包工具 -- **[GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl.md)** — 崩溃监控与回滚 +- **[GeneralUpdate.Core](../doc/GeneralUpdate.Core)** — 核心更新引擎 +- **[入门实战手册](../quickstart/Beginner%20cookbook)** — 从零跑通更新闭环 +- **[GeneralUpdate.Tools](../quickstart/GeneralUpdate.PacketTool)** — 打包工具 +- **[GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl)** — 崩溃监控与回滚 diff --git a/website/docs/guide/Packaging.md b/website/docs/guide/Packaging.md index 0329db7..e8263bf 100644 --- a/website/docs/guide/Packaging.md +++ b/website/docs/guide/Packaging.md @@ -120,7 +120,7 @@ await new GeneralUpdateBootstrap() **避免 C 盘权限问题:** - 建议默认安装到 `%LOCALAPPDATA%` 而非 `C:\Program Files\` -- 如必须安装在 C 盘,参考 [权限指南](./Permission.md) 配置注册表降权 +- 如必须安装在 C 盘,参考 [权限指南](./Permission) 配置注册表降权 **Windows 发布命令:** @@ -280,6 +280,6 @@ jobs: ## 相关资源 - **[GeneralUpdate.Tools](../quickstart/GeneralUpdate.PacketTool)** — 补丁包和配置生成工具 -- **[权限指南](./Permission.md)** — Windows UAC 权限处理 -- **[入门实战手册](../quickstart/Beginner%20cookbook.md)** — 从零跑通更新闭环 -- **[GeneralUpdate.Core](../doc/GeneralUpdate.Core.md)** — 核心更新引擎架构 +- **[权限指南](./Permission)** — Windows UAC 权限处理 +- **[入门实战手册](../quickstart/Beginner%20cookbook)** — 从零跑通更新闭环 +- **[GeneralUpdate.Core](../doc/GeneralUpdate.Core)** — 核心更新引擎架构 diff --git a/website/docs/quickstart/Beginner cookbook.md b/website/docs/quickstart/Beginner cookbook.md index e60f34e..b429b71 100644 --- a/website/docs/quickstart/Beginner cookbook.md +++ b/website/docs/quickstart/Beginner cookbook.md @@ -11,12 +11,12 @@ title: 入门实战手册 | 角色 | 在样例中的位置 | 负责什么 | 深入阅读 | | --- | --- | --- | --- | -| Hub | `src\Hub` | 交互式示例浏览器,通过菜单选择并运行各类更新场景 | [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md) | -| Server | `src\Server` | 返回版本信息、接收更新报告、提供补丁下载 | [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md) | -| Packet | `src\Server\wwwroot\packages` | 可下载的 `.zip` 更新包和 `versions.json` 元数据 | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md) | -| Tools | GeneralUpdate.Tools 仓库 | 生成补丁包、Hash、OSS 清单、manifest 和仿真报告 | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md) | -| Bowl | Hub Samples 中集成 | 监控进程异常并导出失败信息 | [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl.md) | -| Differential | Hub Samples + Core 默认集成 | 对 old/new 文件生成差分,并在更新阶段应用 | [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential.md) | +| Hub | `src\Hub` | 交互式示例浏览器,通过菜单选择并运行各类更新场景 | [GeneralUpdate.Core](../doc/GeneralUpdate.Core) | +| Server | `src\Server` | 返回版本信息、接收更新报告、提供补丁下载 | [GeneralUpdate.Core](../doc/GeneralUpdate.Core) | +| Packet | `src\Server\wwwroot\packages` | 可下载的 `.zip` 更新包和 `versions.json` 元数据 | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool) | +| Tools | GeneralUpdate.Tools 仓库 | 生成补丁包、Hash、OSS 清单、manifest 和仿真报告 | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool) | +| Bowl | Hub Samples 中集成 | 监控进程异常并导出失败信息 | [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl) | +| Differential | Hub Samples + Core 默认集成 | 对 old/new 文件生成差分,并在更新阶段应用 | [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential) | ## Step 1:准备仓库和运行环境 @@ -239,11 +239,11 @@ Hub 提供了 9 个内置样例,覆盖了 GeneralUpdate 的主要使用场景 跑通这条链路后,建议按顺序阅读: -1. [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md):更新策略、事件通知、静默更新、manifest 极简配置。 -2. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md):补丁包、Hash、OSS Config、Simulation。 -3. [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential.md):差分算法、并行处理和 Clean/Dirty。 -4. [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl.md):崩溃监控、备份和失败恢复。 -5. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md):补丁包、Hash、OSS Config、Simulation。 +1. [GeneralUpdate.Core](../doc/GeneralUpdate.Core):更新策略、事件通知、静默更新、manifest 极简配置。 +2. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool):补丁包、Hash、OSS Config、Simulation。 +3. [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential):差分算法、并行处理和 Clean/Dirty。 +4. [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl):崩溃监控、备份和失败恢复。 +5. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool):补丁包、Hash、OSS Config、Simulation。 ## Sample UI diff --git a/website/docs/quickstart/GeneralUpdate.PacketTool.md b/website/docs/quickstart/GeneralUpdate.PacketTool.md index c269482..2bd4907 100644 --- a/website/docs/quickstart/GeneralUpdate.PacketTool.md +++ b/website/docs/quickstart/GeneralUpdate.PacketTool.md @@ -144,7 +144,7 @@ dotnet run --project GeneralUpdate.Tools.csproj - Extension Host 调用 `ExtensionManager.QueryRemoteExtensionsAsync(...)` 获取扩展列表 - 安装时下载 ZIP,读取 `manifest.json` 进行兼容性检查和依赖解析 -- 详见 [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension.md) +- 详见 [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension) --- @@ -189,7 +189,7 @@ dotnet run --project GeneralUpdate.Tools.csproj - 将 `oss_config.json` 上传到 OSS bucket 或静态文件服务器 - OSS 客户端读取此文件发现可用版本,下载后校验 Hash -- 详见 [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md) OSS 更新策略 +- 详见 [GeneralUpdate.Core](../doc/GeneralUpdate.Core) OSS 更新策略 --- @@ -349,7 +349,7 @@ Generate Sample 额外输出: ## 关联文档 -- [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md):Client/Upgrade 更新主流程 -- [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential.md):差分算法 Clean/Dirty 模式 -- [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension.md):扩展包安装与版本管理 -- [入门实战手册](./Beginner%20cookbook.md):从零跑通完整更新闭环 +- [GeneralUpdate.Core](../doc/GeneralUpdate.Core):Client/Upgrade 更新主流程 +- [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential):差分算法 Clean/Dirty 模式 +- [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension):扩展包安装与版本管理 +- [入门实战手册](./Beginner%20cookbook):从零跑通完整更新闭环 diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Bowl.md b/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Bowl.md index fda1f9f..ac9e4a3 100644 --- a/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Bowl.md +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Bowl.md @@ -362,4 +362,4 @@ MyApp/ - [Bowl Sample Code](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/Bowl) - [GeneralUpdate Repository](https://github.com/GeneralLibrary/GeneralUpdate) -- [Dump Guide](../guide/Dump.md) +- [Dump Guide](../guide/Dump) diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Core.md b/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Core.md index b79af76..3092fa4 100644 --- a/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Core.md +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Core.md @@ -98,8 +98,11 @@ sidebar_position: 5 | `ProductId` | `string` | — | Optional | — | Product identifier for multi-product servers | | `UpdatePath` | `string` | `InstallPath` | Optional | Valid directory path | Upgrade process location | | `Bowl` | `string` | `null` | Optional | Valid filename | Auxiliary process name to close before update | -| `Scheme` | `string` | `null` | Optional | `"Bearer"` etc. | Auth scheme | +| `Scheme` | `string` | `null` | Optional | `"Bearer"` etc. | Auth scheme (used with `Token`) | | `Token` | `string` | `null` | Optional | — | Auth token | +| `AuthScheme` | `AuthScheme` | `Hmac` | Optional | `Hmac`, `Bearer`, `Basic` | Auth scheme enum; `Hmac` for GeneralSpacestation server-side signed auth | +| `BasicUsername` | `string` | `null` | Optional | — | HTTP Basic auth username (requires `AuthScheme = Basic`) | +| `BasicPassword` | `string` | `null` | Optional | — | HTTP Basic auth password (requires `AuthScheme = Basic`) | | `Files` | `List` | `null` | Optional | — | Files to skip during update | | `Formats` | `List` | `null` | Optional | — | Extensions to skip during update | | `Directories` | `List` | `null` | Optional | — | Directories to skip during update | @@ -135,7 +138,7 @@ sidebar_position: 5 | `Cancel()` | None | `void` | UI "Cancel Update" button | Triggers internal `CancellationTokenSource` | | `SetConfig(UpdateRequest)` | `configInfo` | `GeneralUpdateBootstrap` | Explicit update configuration | Calls `Validate()` on key fields | | `SetConfig(string)` | `filePath` — JSON config file path | `GeneralUpdateBootstrap` | Read config from file | Supports relative/absolute paths; UTF-8 JSON | -| `SetSource(...)` | `updateUrl`, `appSecretKey`, `reportUrl?`, `scheme?`, `token?` | `GeneralUpdateBootstrap` | Lightweight entry with manifest | Identity info filled by manifest | +| `SetSource(...)` | `updateUrl`, `appSecretKey`, `reportUrl?`, `scheme?`, `token?`, `authScheme?`, `basicUsername?`, `basicPassword?`, `installPath?` | `GeneralUpdateBootstrap` | Lightweight entry with manifest | Identity info filled by manifest; supports HMAC / Bearer / Basic auth methods | | `SetOption(Option, T)` | `option` — key, `value` — value | `GeneralUpdateBootstrap` | Set runtime options | Pass `null` to reset nullable options | | `UseDiffPipeline(Action)` | `configure` — delegate | `GeneralUpdateBootstrap` | Replace or tune diff pipeline | Default used if not called | | `AddListenerUpdateInfo(...)` | `EventHandler` | `GeneralUpdateBootstrap` | Receive server version info | Also fires when no update available | @@ -664,5 +667,5 @@ GeneralTracer.Dispose(); - [GeneralUpdate Repository](https://github.com/GeneralLibrary/GeneralUpdate) - [Samples Code](https://github.com/GeneralLibrary/GeneralUpdate-Samples) - [GeneralUpdate.Tools](https://github.com/GeneralLibrary/GeneralUpdate.Tools) -- [Beginner Cookbook](../quickstart/Beginner%20cookbook.md) -- [Component docs](./GeneralUpdate.Core.md) +- [Beginner Cookbook](../quickstart/Beginner%20cookbook) +- [Component docs](./GeneralUpdate.Core) diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Differential.md b/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Differential.md index ae1d454..c423390 100644 --- a/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Differential.md +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Differential.md @@ -21,7 +21,7 @@ Directory-level comparison, batch patch generation, parallel scheduling, deleted | File-Level Diff Generation | `CleanAsync(oldFile, newFile, patchFile)` — compare old & new files to generate `.patch` | | File-Level Diff Application | `DirtyAsync(oldFile, newFile, patchFile)` — old file + patch → new file | | Pluggable Diff Algorithms | `BsdiffDiffer` (BSDIFF 4.0, suffix sort) and `StreamingHdiffDiffer` (block hash indexing) | -| Pluggable Compression | BZip2 (0x00), Deflate (0x01), .NET 6+ Brotli (0x02) reserved in source | +| Pluggable Compression | BZip2 (0x00), Deflate (0x01), .NET 6+ Brotli (0x02) available via conditional compilation | | BSDIFF Compatible Format | 33-byte extended header (32-byte BSDIFF40 + 1-byte compression format), 32-byte legacy compatible | | Thread Safety | Built-in differ and compression providers support concurrent calls | @@ -87,7 +87,7 @@ Differential is a low-level library with no configuration classes. All parameter | --- | --- | --- | --- | | `BZip2CompressionProvider` | `0x00` | Fully available | Legacy BSDIFF compatible, higher decompression cost | | `DeflateCompressionProvider` | `0x01` | Fully available | Faster decompression, better for client batch apply | -| `BrotliCompressionProvider` | `0x02` | .NET 6+ only (source reserved) | Not recommended for production | +| `BrotliCompressionProvider` | `0x02` | .NET 6+ only (full implementation in source behind `#if NET6_0_OR_GREATER` conditional compilation) | Not included in current `netstandard2.0` package; not recommended for production | ### 3.2 Instance Methods @@ -287,5 +287,5 @@ await new GeneralUpdateBootstrap() - [GeneralUpdate Repository](https://github.com/GeneralLibrary/GeneralUpdate) - [Differential Sample](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/Hub/Samples/DifferentialSample.cs) -- [Core DiffPipeline Docs](GeneralUpdate.Core.md) -- [Packaging Guide](../guide/Packaging.md) +- [Core DiffPipeline Docs](GeneralUpdate.Core) +- [Packaging Guide](../guide/Packaging) diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Drivelution.md b/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Drivelution.md index 09a55a5..7a9444d 100644 --- a/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Drivelution.md +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Drivelution.md @@ -354,4 +354,4 @@ GeneralTracer.SetTracingEnabled(true); // Re-enable for troubleshooting - [Driver Update Sample](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/Hub/Samples/ImDiskQuickInstallSample.cs) - [GeneralUpdate Repository](https://github.com/GeneralLibrary/GeneralUpdate) -- [GeneralUpdate.Drivelution docs](../doc/GeneralUpdate.Drivelution.md) +- [GeneralUpdate.Drivelution docs](../doc/GeneralUpdate.Drivelution) diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md b/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md index 899ecfd..e8f87d6 100644 --- a/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md @@ -518,4 +518,4 @@ Per-extension setting > Global setting > Default (false) - [Extension Management Sample](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/Hub/Samples/ExtensionSample.cs) - [GeneralUpdate Repository](https://github.com/GeneralLibrary/GeneralUpdate) -- [Packaging Guide](../guide/Packaging.md) +- [Packaging Guide](../guide/Packaging) diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/guide/FAQ.md b/website/i18n/en/docusaurus-plugin-content-docs/current/guide/FAQ.md index cb1d12b..b4b015a 100644 --- a/website/i18n/en/docusaurus-plugin-content-docs/current/guide/FAQ.md +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/guide/FAQ.md @@ -279,7 +279,7 @@ Sample projects provide a simple server example. For production, implement your ## Related Resources -- **[GeneralUpdate.Core](../doc/GeneralUpdate.Core.md)** — Core update engine -- **[Beginner Cookbook](../quickstart/Beginner%20cookbook.md)** — Run through the full update loop -- **[GeneralUpdate.Tools](../quickstart/GeneralUpdate.PacketTool.md)** — Packaging tools -- **[GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl.md)** — Crash monitoring & rollback +- **[GeneralUpdate.Core](../doc/GeneralUpdate.Core)** — Core update engine +- **[Beginner Cookbook](../quickstart/Beginner%20cookbook)** — Run through the full update loop +- **[GeneralUpdate.Tools](../quickstart/GeneralUpdate.PacketTool)** — Packaging tools +- **[GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl)** — Crash monitoring & rollback diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/guide/Packaging.md b/website/i18n/en/docusaurus-plugin-content-docs/current/guide/Packaging.md index ad4fb28..561a1c6 100644 --- a/website/i18n/en/docusaurus-plugin-content-docs/current/guide/Packaging.md +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/guide/Packaging.md @@ -110,7 +110,7 @@ await new GeneralUpdateBootstrap() - [NSIS Download](https://nsis.sourceforge.io/Download) - Place main app and upgrade assistant in the same directory - Default install to `%LOCALAPPDATA%` to avoid C drive permission issues -- For C drive installs, see [Permission Guide](./Permission.md) +- For C drive installs, see [Permission Guide](./Permission) ```bash dotnet publish src/MyApp/MyApp.csproj -c Release -r win-x64 --self-contained @@ -216,6 +216,6 @@ Before deploying, verify: ## Related Resources - **[GeneralUpdate.Tools](../quickstart/GeneralUpdate.PacketTool)** — Patch and config generation tool -- **[Permission Guide](./Permission.md)** — Windows UAC permission handling -- **[Beginner Cookbook](../quickstart/Beginner%20cookbook.md)** — Complete update walkthrough -- **[GeneralUpdate.Core](../doc/GeneralUpdate.Core.md)** — Core update engine architecture +- **[Permission Guide](./Permission)** — Windows UAC permission handling +- **[Beginner Cookbook](../quickstart/Beginner%20cookbook)** — Complete update walkthrough +- **[GeneralUpdate.Core](../doc/GeneralUpdate.Core)** — Core update engine architecture diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/quickstart/Beginner cookbook.md b/website/i18n/en/docusaurus-plugin-content-docs/current/quickstart/Beginner cookbook.md index aec4cab..33a8cc2 100644 --- a/website/i18n/en/docusaurus-plugin-content-docs/current/quickstart/Beginner cookbook.md +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/quickstart/Beginner cookbook.md @@ -11,12 +11,12 @@ This cookbook is for first-time GeneralUpdate users. The goal is not to explain | Role | Sample location | Responsibility | Deep dive | | --- | --- | --- | --- | -| Hub | `src\Hub` | Interactive sample browser; run update scenarios via menu | [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md) | -| Server | `src\Server` | Returns version metadata, accepts reports, and serves package downloads | [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md) | -| Packet | `src\Server\wwwroot\packages` | Downloadable `.zip` packages and `versions.json` metadata | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md) | -| Tools | GeneralUpdate.Tools repository | Generates patch packages, hashes, OSS manifests, and simulation reports | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md) | -| Bowl | Integrated in Hub Samples | Monitors process failures and exports failure data | [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl.md) | -| Differential | Hub Samples + Core default integration | Generates old/new binary differences and applies them during updates | [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential.md) | +| Hub | `src\Hub` | Interactive sample browser; run update scenarios via menu | [GeneralUpdate.Core](../doc/GeneralUpdate.Core) | +| Server | `src\Server` | Returns version metadata, accepts reports, and serves package downloads | [GeneralUpdate.Core](../doc/GeneralUpdate.Core) | +| Packet | `src\Server\wwwroot\packages` | Downloadable `.zip` packages and `versions.json` metadata | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool) | +| Tools | GeneralUpdate.Tools repository | Generates patch packages, hashes, OSS manifests, and simulation reports | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool) | +| Bowl | Integrated in Hub Samples | Monitors process failures and exports failure data | [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl) | +| Differential | Hub Samples + Core default integration | Generates old/new binary differences and applies them during updates | [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential) | ## Step 1: Prepare the repository and runtime @@ -239,11 +239,11 @@ Try running each sample and observe the results in the `mock_app` directory. After this flow works, read these pages in order: -1. [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md): update strategies, event notifications, silent updates, and manifest-based minimal configuration. -2. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md): patch packages, Hash, OSS Config, and Simulation. -3. [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential.md): differential algorithms, parallel processing, and Clean/Dirty. -4. [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl.md): crash monitoring, backup, and failure recovery. -5. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md): patch packages, Hash, OSS Config, and Simulation. +1. [GeneralUpdate.Core](../doc/GeneralUpdate.Core): update strategies, event notifications, silent updates, and manifest-based minimal configuration. +2. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool): patch packages, Hash, OSS Config, and Simulation. +3. [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential): differential algorithms, parallel processing, and Clean/Dirty. +4. [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl): crash monitoring, backup, and failure recovery. +5. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool): patch packages, Hash, OSS Config, and Simulation. ## Sample UI diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/quickstart/GeneralUpdate.PacketTool.md b/website/i18n/en/docusaurus-plugin-content-docs/current/quickstart/GeneralUpdate.PacketTool.md index 4466b53..1e1fc9d 100644 --- a/website/i18n/en/docusaurus-plugin-content-docs/current/quickstart/GeneralUpdate.PacketTool.md +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/quickstart/GeneralUpdate.PacketTool.md @@ -138,7 +138,7 @@ Example `manifest.json`: - Extension Host calls `ExtensionManager.QueryRemoteExtensionsAsync(...)` to list available extensions - On install, downloads the ZIP, reads `manifest.json` for compatibility checks and dependency resolution -- See [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension.md) for details +- See [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension) for details --- @@ -183,7 +183,7 @@ If you use OSS mode updates (static file server), you need to maintain a `versio - Upload `oss_config.json` to your OSS bucket or static file server - OSS client reads this file to discover versions and validates Hash after download -- See [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md) OSS update strategy +- See [GeneralUpdate.Core](../doc/GeneralUpdate.Core) OSS update strategy --- @@ -338,7 +338,7 @@ This sequence chains the six modules into a complete release pipeline: ## Related docs -- [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md): Client/Upgrade main update flow -- [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential.md): Differential algorithm Clean/Dirty modes -- [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension.md): Extension install and version management -- [Beginner cookbook](./Beginner%20cookbook.md): Complete end-to-end update walkthrough +- [GeneralUpdate.Core](../doc/GeneralUpdate.Core): Client/Upgrade main update flow +- [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential): Differential algorithm Clean/Dirty modes +- [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension): Extension install and version management +- [Beginner cookbook](./Beginner%20cookbook): Complete end-to-end update walkthrough diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Bowl.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Bowl.md index b15cfd1..d490a12 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Bowl.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Bowl.md @@ -417,4 +417,4 @@ MyApp/ - [Bowl 示例代码](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/Bowl) - [GeneralUpdate 仓库](https://github.com/GeneralLibrary/GeneralUpdate) -- [Dump 指南](../guide/Dump.md) +- [Dump 指南](../guide/Dump) diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Core.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Core.md deleted file mode 100644 index 556adc1..0000000 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Core.md +++ /dev/null @@ -1,1206 +0,0 @@ ---- -sidebar_position: 5 ---- - -# GeneralUpdate.Core - -**命名空间:** `GeneralUpdate.Core` | **主要入口:** `GeneralUpdateBootstrap` | **NuGet 包:** `GeneralUpdate.Core` - -`GeneralUpdate.Core` 是 GeneralUpdate 的更新执行核心,重点提供可编程的启动器、配置模型、事件模型、下载子系统扩展点、生命周期钩子、状态上报、差分管道和平台策略扩展。本页聚焦组件 API、属性和扩展方式;完整端到端上手流程会放到 cookbook 中。 - -## 文档大纲与知识点导航 {#knowledge-map} - -如果你是第一次阅读 Core 文档,可以先看这个导航,再跳到对应知识点。本文按照“入口与配置 -> 执行策略 -> 差分/下载/并发 -> 扩展点 -> 工具链关系”的顺序组织。 - -| 你想了解什么 | 推荐阅读 | -| --- | --- | -| Core 到底负责什么、不负责什么 | [组件能力边界](#组件能力边界) | -| 如何启动一次标准更新 | [入口类:GeneralUpdateBootstrap](#入口类generalupdatebootstrap)、[标准更新策略](#standard-update-strategy) | -| 如何用极简配置接入更新 | [generalupdate.manifest.json](#应用身份清单generalupdatemanifestjson)、[极简配置理念](#极简配置理念)、[配合引导类使用](#配合引导类使用) | -| `Client`、`Upgrade`、`OssClient`、`OssUpgrade` 有什么区别 | [执行策略总览](#execution-strategies) | -| 静默更新到底什么时候下载、什么时候替换 | [静默更新策略](#silent-update-strategy) | -| 差分算法有哪些,如何选择 | [差分算法与补丁管道](#differential-pipeline) | -| 下载和差分如何并发、多线程 | [下载并发与差分并行](#download-diff-concurrency) | -| 如何接收更新过程事件通知 | [事件 API](#事件-api) | -| 如何关闭日志降低性能损耗 | [日志与性能](#logging-performance) | -| 如何扩展下载、校验、认证、Hook 或平台策略 | [扩展点总览](#扩展点总览) | -| Tools 生成的产物在 Core 中怎么消费 | [与 GeneralUpdate.Tools 的关系](#与-generalupdatetools-的关系) | - -## 组件能力边界 - -Core 负责“执行更新”,不负责生成更新包,也不直接管理服务端后台。 - -| 能力 | Core 是否负责 | 说明 | -| --- | --- | --- | -| 读取更新配置 | 是 | 通过 `UpdateRequest`、配置文件、`SetSource` 或 IPC 恢复运行参数。 | -| 检查服务端版本 | 是 | `Client` / `OssClient` 角色会读取版本清单并生成下载计划。 | -| 下载更新包 | 是 | 可替换下载来源、执行器、重试策略、后处理管道或完整编排器。 | -| 校验与应用补丁 | 是 | 支持 Hash 校验、压缩包处理、差分补丁管道。 | -| 文件替换与重启应用 | 是 | `Upgrade` / `OssUpgrade` 角色用于独立升级程序。 | -| 生成差分包 | 否 | 推荐使用 `GeneralUpdate.Tools`。 | - -## 入口类:GeneralUpdateBootstrap - -`GeneralUpdateBootstrap` 是 Core 的主要门面类。它继承 `AbstractBootstrap`,因此同时拥有自身方法和基类提供的扩展注册方法。 - -```csharp -using GeneralUpdate.Core; - -var bootstrap = new GeneralUpdateBootstrap(); -``` - -### 方法总览 - -| 方法 | 用途 | 常用场景 | -| --- | --- | --- | -| `LaunchAsync()` | 按当前 `Option.AppType` 启动更新流程。 | 所有 Core 使用场景最终都会调用。 | -| `Cancel()` | 请求取消当前更新操作。 | UI 中提供“取消更新”按钮。 | -| `SetConfig(UpdateRequest)` | 使用强类型对象配置更新。 | 主程序内显式配置更新参数。 | -| `SetConfig(string)` | 从 JSON 文件读取 `UpdateRequest`。 | 将更新参数放到 `update_config.json` 或自定义配置文件。 | -| `SetSource(...)` | 只提供更新地址、密钥、报告地址等基础参数。 | 零配置/轻配置入口。 | -| `SetOption(Option, T)` | 设置运行时选项。 | 设置角色、超时、并发、差分、静默更新等。 | -| `UseDiffPipeline(Action)` | 自定义差分补丁管道。 | 替换 differ、调整并行度、接入补丁进度。 | -| `AddListenerUpdatePrecheck(Func)` | 下载前预检查。 | 检查磁盘空间、网络状态或弹窗确认。 | -| `AddListener...` | 注册单个事件回调。 | 更新 UI、写日志、上报监控。 | -| `AddEventListener()` | 批量注册事件监听器。 | 将事件处理封装成类。 | - -### LaunchAsync - -```csharp -public Task LaunchAsync() -``` - -`LaunchAsync` 会读取 `Option.AppType` 并选择对应策略: - -| `Option.AppType` | 策略 | 说明 | -| --- | --- | --- | -| `AppType.Client` | `ClientStrategy` | 主程序侧:检查版本、下载包、准备升级上下文、启动升级程序。 | -| `AppType.Upgrade` | `UpdateStrategy` | 升级程序侧:读取 IPC 上下文并执行文件替换。 | -| `AppType.OssClient` | `OssStrategy` | OSS 主程序侧更新流程。 | -| `AppType.OssUpgrade` | `OssStrategy` | OSS 升级程序侧更新流程。 | - -示例:独立升级程序入口。 - -```csharp -await new GeneralUpdateBootstrap() - .SetOption(Option.AppType, AppType.Upgrade) - .AddListenerException((_, e) => Console.WriteLine(e.Exception)) - .LaunchAsync(); -``` - -> 当升级程序由主程序启动时,Core 会通过加密文件 IPC 自动恢复更新上下文,通常不需要在升级程序里再次调用 `SetConfig`。 - -## 执行策略总览 {#execution-strategies} - -Core 内置三类上层执行策略:标准更新策略、OSS 更新策略和静默更新策略。它们不是互相独立的 API,而是由 `LaunchAsync()` 根据 `Option.AppType`、`Option.Silent` 和当前配置自动选择。 - -| 策略 | 触发条件 | 主要角色 | 适用场景 | -| --- | --- | --- | --- | -| [标准更新策略](#standard-update-strategy) | `Option.AppType = AppType.Client` 或 `AppType.Upgrade`,且 `Option.Silent = false` | 主程序检查/下载,升级程序替换文件 | 有服务端版本检查 API、需要标准更新和回写版本的桌面应用 | -| [OSS 更新策略](#oss-update-strategy) | `Option.AppType = AppType.OssClient` 或 `AppType.OssUpgrade` | 主程序下载 OSS 版本配置,升级程序下载并解压资源 | 版本配置和包都托管在对象存储/CDN 的应用 | -| [静默更新策略](#silent-update-strategy) | `Option.AppType = AppType.Client` 且 `Option.Silent = true` | 主程序后台轮询,退出时启动升级程序 | 希望用户使用期间无打扰下载,退出或合适时机再替换 | - -### 标准更新策略 {#standard-update-strategy} - -标准更新由 `ClientStrategy` 和 `UpdateStrategy` 配合完成。`ClientStrategy` 运行在主程序中,负责发现本地清单、请求服务端版本、生成下载计划、下载更新包、准备 IPC 上下文并启动升级程序;`UpdateStrategy` 运行在独立升级程序中,负责读取 IPC 上下文、解压、应用差分补丁、替换文件、回写版本并按需启动主程序。 - -标准流程的核心顺序如下: - -1. 主程序调用 `SetConfig(...)` 或 `SetSource(...)` 后执行 `LaunchAsync()`。 -2. Core 自动读取安装目录下的 `generalupdate.manifest.json`,补齐未显式提供的 `MainAppName`、`UpdateAppName`、`ClientVersion`、`UpgradeClientVersion`、`ProductId`、`InstallPath` 等身份字段。 -3. `ClientStrategy` 使用 `DownloadSource` 获取服务端资源列表,并分别比较主程序版本和升级程序版本。 -4. `DownloadPlanBuilder` 过滤冻结包、按版本排序、检查 `MinClientVersion`,形成下载计划。 -5. 下载阶段通过 `IDownloadOrchestrator` 批量下载资源,默认支持并发、断点续传、重试和 SHA256 校验。 -6. Core 按资源 `AppType` 拆分为升级程序包和主程序包,再根据场景分派。 -7. 升级程序包可以在主程序侧先应用并回写 `UpgradeClientVersion`;主程序包会通过加密 IPC 交给升级程序替换。 -8. 升级程序完成替换后,Core 基于清单体系自动回写 `ClientVersion`,开发者不需要在业务代码中维护本地版本号。 - -| 场景 | 判断结果 | Core 行为 | -| --- | --- | --- | -| `None` | 主程序和升级程序都无需更新 | 分发“无更新”事件并结束。 | -| `UpgradeOnly` | 只有升级程序需要更新 | 主程序下载升级程序包,直接应用到升级程序目录,回写 `UpgradeClientVersion`,主程序继续运行。 | -| `MainOnly` | 只有主程序需要更新 | 主程序下载主程序包,写入 IPC 上下文,启动升级程序替换主程序文件。 | -| `Both` | 主程序和升级程序都需要更新 | 先更新升级程序并回写 `UpgradeClientVersion`,再把主程序包交给新的升级程序处理。 | - -```csharp -await new GeneralUpdateBootstrap() - .SetSource( - updateUrl: "https://update.example.com/api/upgrade/verification", - appSecretKey: "your-app-secret", - reportUrl: "https://update.example.com/api/upgrade/report") - .SetOption(Option.AppType, AppType.Client) - .SetOption(Option.PatchEnabled, true) - .SetOption(Option.DiffMode, DiffMode.Parallel) - .SetOption(Option.MaxConcurrency, 4) - .LaunchAsync(); -``` - -### OSS 更新策略 {#oss-update-strategy} - -OSS 更新由同一个 `OssStrategy` 根据角色分成 `OssClient` 和 `OssUpgrade` 两段。它适合把版本配置 JSON 和更新包放在 OSS、S3、MinIO、CDN 或静态文件服务器上,不依赖标准服务端版本检查 API。 - -| 角色 | 本地行为 | 关键配置 | -| --- | --- | --- | -| `AppType.OssClient` | 从 `UpdateUrl` 下载 OSS 版本配置到安装目录,比较远端最新版本和本地 `ClientVersion`,需要更新时启动升级程序并退出。 | `UpdateUrl` 指向版本配置文件地址;`MainAppName` / `UpdateAppName` 可由 manifest 提供。 | -| `AppType.OssUpgrade` | 读取本地版本配置或自定义 `DownloadSource`,筛选高于本地版本的资源,下载到安装目录,解压 ZIP,删除压缩包,启动主程序并退出。 | 安装目录可写;资源列表中的版本号必须可比较。 | - -OSS 版本配置文件会保存为 `{MainAppName}_versions.json` 或 `{UpdateAppName}_versions.json`。如果注册了 `DownloadSource()`,OSS 升级侧可以跳过默认文件读取逻辑,改为由你的下载源返回资源列表;如果注册了 `DownloadOrchestrator()`,下载过程也可以完全替换。 - -```csharp -await new GeneralUpdateBootstrap() - .SetSource( - updateUrl: "https://cdn.example.com/myapp_versions.json", - appSecretKey: "oss-mode-secret") - .SetOption(Option.AppType, AppType.OssClient) - .LaunchAsync(); -``` - -```csharp -await new GeneralUpdateBootstrap() - .SetOption(Option.AppType, AppType.OssUpgrade) - .LaunchAsync(); -``` - -### 静默更新策略 {#silent-update-strategy} - -静默更新只在 `AppType.Client` 下生效。启用后,`LaunchAsync()` 会进入静默启动分支,创建和标准更新相同的 `ClientStrategy`,但把 `LaunchAfterPrepare` 设为 `false`,再交给 `SilentPollOrchestrator` 做后台轮询。 - -静默模式不会重新实现更新逻辑;它只是把“检查和下载”放到后台,把“启动升级程序替换文件”延后到进程退出时。这样用户可以继续使用当前进程,更新包先准备好,真正替换发生在应用退出之后。 - -| 阶段 | 标准更新 | 静默更新 | -| --- | --- | --- | -| 版本检查 | 用户触发后立即执行一次 | 后台按 `Option.SilentPollIntervalMinutes` 周期执行 | -| 下载 | 发现更新后立即下载 | 发现更新后后台下载 | -| 启动升级程序 | 主程序准备完成后立即启动 | 主程序退出时由 `ProcessExit` 处理启动 | -| 用户体验 | 适合显式“检查更新/立即更新” | 适合无打扰准备更新 | - -```csharp -await new GeneralUpdateBootstrap() - .SetSource( - updateUrl: "https://update.example.com/api/upgrade/verification", - appSecretKey: "your-app-secret") - .SetOption(Option.AppType, AppType.Client) - .SetOption(Option.Silent, true) - .SetOption(Option.SilentPollIntervalMinutes, 30) - .SetOption(Option.LaunchClientAfterUpdate, true) - .LaunchAsync(); -``` - -静默更新仍然会使用你注册的 `IUpdateHooks`、`IUpdateReporter`、下载扩展、证书策略、认证策略和差分管道。需要注意的是,静默模式适合“下载准备无感知”,不等于“文件替换无感知”;主程序文件仍应由独立升级程序在主程序退出后替换。 - -### Cancel - -```csharp -public void Cancel() -``` - -`Cancel` 会触发内部 `CancellationTokenSource`,更新策略会在安全检查点观察取消请求。适合 UI 应用把 bootstrap 保存为字段后绑定取消按钮。 - -```csharp -private GeneralUpdateBootstrap? _bootstrap; - -async Task StartUpdateAsync(UpdateRequest request) -{ - _bootstrap = new GeneralUpdateBootstrap() - .SetConfig(request) - .AddListenerException((_, e) => Console.WriteLine(e.Exception)); - - await _bootstrap.LaunchAsync(); -} - -void CancelUpdate() -{ - _bootstrap?.Cancel(); -} -``` - -### SetConfig(UpdateRequest) - -```csharp -public GeneralUpdateBootstrap SetConfig(UpdateRequest configInfo) -``` - -`SetConfig(UpdateRequest)` 会调用 `UpdateRequest.Validate()`,并把外部配置映射为内部 `UpdateContext`。当角色不是 `AppType.Upgrade` 时,它还会初始化临时目录和黑名单匹配器。 - -```csharp -using GeneralUpdate.Core; -using GeneralUpdate.Core.Configuration; - -var request = new UpdateRequest -{ - UpdateUrl = "https://update.example.com/api/upgrade/verification", - ReportUrl = "https://update.example.com/api/upgrade/report", - UpdateAppName = "UpgradeSample.exe", - MainAppName = "ClientSample.exe", - InstallPath = AppDomain.CurrentDomain.BaseDirectory, - ClientVersion = "1.0.0", - AppSecretKey = "your-app-secret", - ProductId = "your-product-id", - Files = new List { "appsettings.json" }, - Formats = new List { ".log", ".tmp" }, - Directories = new List { "logs", "cache" } -}; - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .SetOption(Option.AppType, AppType.Client) - .LaunchAsync(); -``` - -### SetConfig(string) - -```csharp -public GeneralUpdateBootstrap SetConfig(string filePath) -``` - -`SetConfig(string)` 从 UTF-8 JSON 文件读取 `UpdateRequest`。如果只传文件名,会从当前应用基目录解析;如果传相对或绝对路径,会按路径解析。 - -```json -{ - "updateUrl": "https://update.example.com/api/upgrade/verification", - "reportUrl": "https://update.example.com/api/upgrade/report", - "updateAppName": "UpgradeSample.exe", - "mainAppName": "ClientSample.exe", - "installPath": "C:\\Program Files\\MyApp", - "clientVersion": "1.0.0", - "appSecretKey": "your-app-secret", - "productId": "your-product-id" -} -``` - -```csharp -await new GeneralUpdateBootstrap() - .SetConfig("update_config.json") - .SetOption(Option.AppType, AppType.Client) - .LaunchAsync(); -``` - -### SetSource - -```csharp -public GeneralUpdateBootstrap SetSource( - string updateUrl, - string appSecretKey, - string? reportUrl = null, - string? scheme = null, - string? token = null) -``` - -`SetSource` 是轻配置入口,适合把应用身份信息放到 `generalupdate.manifest.json`,只在代码中指定服务端入口和密钥。 - -```csharp -await new GeneralUpdateBootstrap() - .SetSource( - updateUrl: "https://update.example.com/api/upgrade/verification", - appSecretKey: "your-app-secret", - reportUrl: "https://update.example.com/api/upgrade/report", - scheme: "Bearer", - token: "access-token") - .SetOption(Option.AppType, AppType.Client) - .LaunchAsync(); -``` - -### UseDiffPipeline - -```csharp -public GeneralUpdateBootstrap UseDiffPipeline(Action? configure) -``` - -`UseDiffPipeline` 用于替换或调整差分补丁管道。未调用时,引导类会创建默认管道:`BsdiffDiffer`、`DefaultCleanMatcher`、`DefaultDirtyMatcher`、并行度 `2`,并接入 Core 的差分进度事件。关于算法差异、补丁阶段和并发设置,请看 [差分算法与补丁管道](#differential-pipeline)。 - -```csharp -using GeneralUpdate.Core.Differential; -using GeneralUpdate.Core.Models; -using GeneralUpdate.Core.Pipeline; -using GeneralUpdate.Differential.Differ; - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .UseDiffPipeline(builder => - { - builder - .UseDiffer(new StreamingHdiffDiffer()) - .UseCleanMatcher(new DefaultCleanMatcher()) - .UseDirtyMatcher(new DefaultDirtyMatcher()) - .WithParallelism(4) - .WithStopOnFirstError(true) - .WithProgress(new Progress(p => - { - Console.WriteLine($"{p.Completed}/{p.Total}: {p.FileName}"); - })); - }) - .SetOption(Option.PatchEnabled, true) - .LaunchAsync(); -``` - -## 差分算法与补丁管道 {#differential-pipeline} - -Core 的差分能力分两层:`IBinaryDiffer` 负责“单个文件如何生成/应用补丁”,`DiffPipeline` 负责“目录中哪些文件需要补丁、哪些文件是新增/删除、如何并行处理多个文件”。普通使用者只需要打开 `Option.PatchEnabled`;需要调优性能或兼容性时,再通过 `UseDiffPipeline(...)` 调整。 - -### 差分算法类型 - -| 算法/实现 | 默认位置 | 特点 | 适合场景 | -| --- | --- | --- | --- | -| `BsdiffDiffer` | `GeneralUpdateBootstrap` 默认使用 | 经典 BSDIFF 4.0 算法,默认使用 BZip2 压缩,补丁格式兼容性强。 | 追求稳定兼容、已有包体系基于 BSDIFF 的项目。 | -| `StreamingHdiffDiffer` | `DiffPipeline` 直接构造时的默认 differ;也可通过 `UseDiffPipeline` 显式选择 | 使用块级 Hash 索引做候选匹配,典型复杂度更低,默认使用 Deflate,并生成可由 Dirty 阶段读取的 BSDIFF 兼容补丁格式。 | 大文件较多、希望降低生成补丁时内存和 CPU 压力的项目。 | -| 自定义 `IBinaryDiffer` | 通过 `UseDiffPipeline(builder => builder.UseDiffer(...))` 接入 | 完全替换单文件差分算法。 | 企业内部已有补丁格式、加密补丁或专用二进制差分算法。 | - -`BsdiffDiffer` 还支持替换压缩提供器:`BZip2CompressionProvider` 是兼容默认值,`DeflateCompressionProvider` 更偏向速度,`.NET 6+` 可使用 `BrotliCompressionProvider` 在压缩率和解压速度之间取得更好平衡。选择压缩提供器时要确保生成补丁和应用补丁的运行时都能识别对应格式版本。 - -```csharp -using GeneralUpdate.Core.Pipeline; -using GeneralUpdate.Differential.Abstractions; -using GeneralUpdate.Differential.Differ; - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .UseDiffPipeline(builder => - { - builder - .UseDiffer(new BsdiffDiffer(new DeflateCompressionProvider())) - .WithParallelism(4); - }) - .SetOption(Option.PatchEnabled, true) - .LaunchAsync(); -``` - -### Clean 和 Dirty 两个阶段 - -| 阶段 | 方法 | 运行位置 | 作用 | -| --- | --- | --- | --- | -| Clean | `DiffPipeline.CleanAsync(oldDir, newDir, patchDir)` | 发布侧/工具侧 | 对比旧版本和新版本目录,生成 `.patch` 文件,复制新增文件,并写入 `generalupdate.delete.json` 删除清单。 | -| Dirty | `DiffPipeline.DirtyAsync(appDir, patchDir)` | 客户端升级侧 | 读取补丁目录,把 `.patch` 应用到旧文件,复制新增文件,按删除清单移除旧文件。 | - -Core 主要消费 Dirty 阶段;差分包生成建议交给 `GeneralUpdate.Tools` 或发布流水线完成。标准更新中,下载完成后升级程序会先解压包,再在 `PatchEnabled = true` 时通过 `PatchMiddleware` 调用 `DiffPipeline.DirtyAsync(...)` 应用补丁。 - -### 下载并发与差分并行 {#download-diff-concurrency} - -Core 支持两个层面的多线程能力:下载阶段可以并发下载多个更新资源,差分阶段可以并行处理多个文件补丁。内置标准流程是“先完成当前下载计划,再进入解压/差分/替换阶段”;如果你需要把下载和应用做成更细粒度的流水线,可以通过自定义 `IDownloadOrchestrator` 或自定义 `IStrategy` 接管。 - -| 层级 | 控制 API | 默认行为 | 说明 | -| --- | --- | --- | --- | -| 批量下载并发 | `Option.DiffMode` + `Option.MaxConcurrency` | `DiffMode.Serial` 会强制下载并发为 `1`;`DiffMode.Parallel` 使用 `MaxConcurrency`,并被限制在 `1` 到 `Environment.ProcessorCount * 2` 之间。 | 由 `DefaultDownloadOrchestrator` 使用 `SemaphoreSlim` 控制,支持重试、断点续传和校验。 | -| 差分文件并行 | `UseDiffPipeline(...WithParallelism(n))` | 引导类默认 `2`。 | `DiffPipeline` 对多个文件创建任务,并用 `SemaphoreSlim` 限制同时生成/应用补丁的文件数。 | -| 下载后处理 | `DownloadPipeline()` | 默认 SHA256 校验。 | 每个资源下载成功后执行,可替换为解密、扫描、二次校验等。 | - -```csharp -await new GeneralUpdateBootstrap() - .SetConfig(request) - .SetOption(Option.DiffMode, DiffMode.Parallel) - .SetOption(Option.MaxConcurrency, 6) - .UseDiffPipeline(builder => - { - builder - .UseDiffer(new StreamingHdiffDiffer()) - .WithParallelism(4); - }) - .LaunchAsync(); -``` - -并发值不是越大越好。网络慢但磁盘快时可以提高 `Option.MaxConcurrency`;补丁文件很多且磁盘是 SSD 时可以提高 `WithParallelism`;机械硬盘、低配终端或后台静默更新建议降低并发,避免影响主程序响应。 - -## 配置模型:UpdateRequest - -`UpdateRequest` 是外部调用者最常用的配置对象。它继承 `UpdateConfiguration`,并在 `Validate()` 中检查关键字段。 - -### 必填或强烈建议配置的属性 - -| 属性 | 说明 | -| --- | --- | -| `UpdateUrl` | 更新检查 API 地址。必须是绝对 URL。 | -| `UpdateAppName` | 升级程序文件名,默认 `Update.exe`。如果你的升级程序叫 `UpgradeSample.exe`,必须显式设置。 | -| `MainAppName` | 主程序文件名。用于升级后重新启动,也用于识别要更新的应用。 | -| `ClientVersion` | 当前主程序版本。 | -| `AppSecretKey` | 应用密钥,用于和服务端约定认证。 | -| `InstallPath` | 应用安装目录,默认当前应用基目录。生产环境建议显式设置。 | - -### 可选属性 - -| 属性 | 说明 | -| --- | --- | -| `ReportUrl` | 更新状态上报 API。 | -| `UpdateLogUrl` | 更新日志页面地址。 | -| `UpgradeClientVersion` | 升级程序自身版本。 | -| `ProductId` | 产品标识,同一服务端管理多个产品时使用。 | -| `UpdatePath` | 升级程序所在目录;为空时使用 `InstallPath`。 | -| `Bowl` | 更新前需要关闭的辅助进程名。 | -| `Scheme` / `Token` | 请求认证信息,可与内置认证提供器配合。 | -| `Files` | 更新时跳过的指定文件。 | -| `Formats` | 更新时跳过的扩展名,例如 `.log`。 | -| `Directories` | 更新时跳过的目录。 | - -### 使用 UpdateRequestBuilder - -`UpdateRequestBuilder` 提供链式构建 API,并在 `Build()` 时执行校验。 - -```csharp -using GeneralUpdate.Core.Configuration; - -var request = new UpdateRequestBuilder() - .SetUpdateUrl("https://update.example.com/api/upgrade/verification") - .SetReportUrl("https://update.example.com/api/upgrade/report") - .SetUpgradeAppName("UpgradeSample.exe") - .SetMainAppName("ClientSample.exe") - .SetClientVersion("1.0.0") - .SetAppSecretKey("your-app-secret") - .SetProductId("your-product-id") - .SetInstallPath(AppDomain.CurrentDomain.BaseDirectory) - .SetFiles(new List { "appsettings.json" }) - .SetFormats(new List { ".log", ".tmp" }) - .SetDirectories(new List { "logs" }) - .Build(); -``` - -`UpdateRequestBuilder.Create()` 会尝试从应用运行目录的 `update_config.json` 读取配置。如果文件不存在,会抛出 `FileNotFoundException`。 - -```csharp -var request = UpdateRequestBuilder.Create().Build(); -``` - -## 应用身份清单:generalupdate.manifest.json - -`generalupdate.manifest.json` 是由 `GeneralUpdate.Tools` 生成、由 Core 消费的应用身份清单。它的核心价值是**帮开发者节约接入和维护时间**:Tools 把“主程序叫什么、当前版本是多少、升级程序叫什么、产品标识是什么、升级程序放在哪个目录”等稳定元数据生成到清单里,Core 在运行时自动消费这些信息,业务代码只需要补充服务端地址、密钥、令牌等运行时或敏感参数。 - -换句话说,使用 manifest 后,接入 GeneralUpdate 不再需要手写一大段完整 `UpdateRequest`。发布时让 Tools 生成 `generalupdate.manifest.json`,运行时再配少量敏感信息,就可以直接启动更新流程。这是 Core 推荐的极简配置方式。 - -推荐把它放在应用安装目录,也就是 `UpdateRequest.InstallPath` 指向的目录。默认情况下 `InstallPath` 是 `AppDomain.CurrentDomain.BaseDirectory`,因此普通桌面应用通常把清单放在主程序输出目录根部。 - -```text -MyProduct/ -├─ ClientSample.exe -├─ generalupdate.manifest.json -└─ update/ - └─ UpgradeSample.exe -``` - -### 清单结构 - -Tools 生成的 JSON 使用小驼峰字段名,Core 中对应类型是 `ManifestInfo`。 - -```json -{ - "mainAppName": "ClientSample.exe", - "clientVersion": "1.0.0", - "appType": "Client", - "updateAppName": "UpgradeSample.exe", - "upgradeClientVersion": "1.0.0", - "productId": "sample-product", - "updatePath": "update/" -} -``` - -| JSON 字段 | Core 字段 | 说明 | -| --- | --- | --- | -| `mainAppName` | `MainAppName` | 主程序可执行文件名。升级完成后用于重新启动主程序,也用于识别当前产品。 | -| `clientVersion` | `ClientVersion` | 当前主程序版本。Core 用它向服务端询问是否有主程序更新。 | -| `appType` | `AppType` | 当前进程角色字符串,例如 `Client`、`Upgrade`、`OssClient`、`OssUpgrade`。 | -| `updateAppName` | `UpdateAppName` | 升级程序文件名,默认 `Update.exe`。 | -| `upgradeClientVersion` | `UpgradeClientVersion` | 升级程序自身版本。Core 用它判断是否需要先更新升级程序。 | -| `productId` | `ProductId` | 产品标识。一个更新服务管理多个产品时用于区分产品。 | -| `updatePath` | `UpdatePath` | 升级程序所在目录;可以是相对 `InstallPath` 的目录,例如 `update/`。 | - -清单刻意不包含 `UpdateUrl`、`ReportUrl`、`AppSecretKey`、`Scheme`、`Token` 等服务端和认证信息。这样可以让 Tools 负责构建可发布的身份元数据,而密钥仍由应用代码、配置中心或部署环境提供。 - -### 极简配置理念 {#极简配置理念} - -manifest 体系把更新配置拆成两部分: - -| 配置类型 | 由谁提供 | 为什么这样拆 | -| --- | --- | --- | -| 稳定身份信息 | `GeneralUpdate.Tools` 生成到 `generalupdate.manifest.json` | 这些字段来自项目、版本和发布目录,重复手写容易出错,也会增加每个应用接入更新的时间。 | -| 运行时/敏感信息 | 应用代码、配置中心、环境变量或部署系统提供 | 服务端地址、密钥、Token 可能因环境变化,也不应该由 Tools 固化到可发布清单里。 | - -因此最常见的接入路径是: - -1. 用 `GeneralUpdate.Tools` 生成并随应用发布 `generalupdate.manifest.json`。 -2. 在应用启动更新时只配置 `UpdateUrl`、`AppSecretKey`、`ReportUrl`,以及必要的认证信息。 -3. 让 `GeneralUpdateBootstrap` 在内部读取 manifest,自动补齐应用身份、版本、升级程序位置,并在更新成功后回写本地版本。 - -这种方式把开发者需要关心的配置压缩到“敏感信息 + 少量运行选项”,既减少样板代码,也避免多个应用重复维护主程序名、升级程序名、本地版本号和产品标识。 - -### Tools 如何生成清单 - -`GeneralUpdate.Tools` 的配置生成流程会解析主程序和升级程序的 `.csproj`,校验版本号,然后写出 `generalupdate.manifest.json`。 - -| Tools 阶段 | 作用 | -| --- | --- | -| `CsprojParseStep` | 解析主程序 `.csproj`;如果提供升级程序 `.csproj`,也会一起解析。 | -| `SemverValidateStep` | 校验 `ClientVersion` 和 `UpgradeClientVersion` 必须符合 semver,例如 `1.0.0`。 | -| `ManifestBuildStep` | 如果 UI 中没有手动填写 `MainAppName` / `UpdateAppName`,使用 `.csproj` 的 `AssemblyName` 补齐。 | -| `FileEmitStep` | 把清单写到输出目录,文件名固定为 `generalupdate.manifest.json`。 | - -配置界面的发布样例流程还会调用 `SamplePublisherService.PublishAsync(...)`,把主程序输出、升级程序输出和清单一起组织到可运行样例目录中。因此新手不需要从零手写完整 `UpdateRequest`,可以先用 Tools 生成清单,再在应用代码中补充服务端入口和密钥。 - -### 配合引导类使用 - -使用清单后,业务代码不需要关心 `MainAppName`、`ClientVersion`、`UpdateAppName`、`UpgradeClientVersion`、`ProductId`、`UpdatePath` 这些身份字段,也不需要手动读取 `generalupdate.manifest.json`。引导类启动更新流程时会在内部读取 `InstallPath/generalupdate.manifest.json`,并把清单中的应用身份信息带入后续的版本检查、下载、启动升级程序和版本回写流程。 - -默认安装目录就是当前应用目录时,只需要把服务端入口和密钥传给 `SetSource`: - -```csharp -await new GeneralUpdateBootstrap() - .SetSource( - updateUrl: "https://update.example.com/api/upgrade/verification", - appSecretKey: "your-app-secret", - reportUrl: "https://update.example.com/api/upgrade/report") - .SetOption(Option.AppType, AppType.Client) - .LaunchAsync(); -``` - -如果应用的实际安装目录不是当前进程基目录,只需要在 `UpdateRequest` 中补充 `InstallPath`,仍然不需要把清单中的身份字段重复写进代码: - -```csharp -using GeneralUpdate.Core; -using GeneralUpdate.Core.Configuration; - -var request = new UpdateRequest -{ - UpdateUrl = "https://update.example.com/api/upgrade/verification", - ReportUrl = "https://update.example.com/api/upgrade/report", - AppSecretKey = "your-app-secret", - InstallPath = @"C:\Program Files\MyProduct" -}; - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .SetOption(Option.AppType, AppType.Client) - .LaunchAsync(); -``` - -推荐的职责拆分是: - -| 由 manifest 提供 | 由代码或环境提供 | -| --- | --- | -| `MainAppName`、`ClientVersion`、`UpdateAppName`、`UpgradeClientVersion`、`ProductId`、`UpdatePath` | `UpdateUrl`、`ReportUrl`、`AppSecretKey`、`Scheme`、`Token`、事件、扩展点、运行选项 | - -### 版本回写 - -在 `generalupdate.manifest.json` 体系下,清单同时也是本地版本状态文件。开发者只需要在首次发布时通过 Tools 生成清单,不需要在每次更新完成后再写业务代码去修改本地版本号。更新成功后,Core 会把已应用的新版本自动写回安装目录下的同一个 `generalupdate.manifest.json`: - -| 场景 | 回写字段 | -| --- | --- | -| 主程序更新完成 | `ClientVersion` | -| 升级程序自身更新完成 | `UpgradeClientVersion` | - -这样下一次轮询或启动时,引导类会基于清单中的最新本地版本继续向服务端验证,而不是继续使用打包时的旧版本。回写的意义是把“本地版本号维护”收进 Core 的更新流程里,避免开发者在应用代码中额外维护 `ClientVersion` 或 `UpgradeClientVersion`。这个行为依赖安装目录可写;如果应用安装在受限目录,需要确保升级程序拥有写入清单的权限。 - -## 运行选项:Option - -Core 使用强类型 `Option` 注册运行时选项,并通过 `SetOption` 设置值。 - -```csharp -await new GeneralUpdateBootstrap() - .SetConfig(request) - .SetOption(Option.AppType, AppType.Client) - .SetOption(Option.MaxConcurrency, 4) - .SetOption(Option.VerifyChecksum, true) - .LaunchAsync(); -``` - -| 选项 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| `Option.AppType` | `AppType` | `Client` | 当前进程角色。 | -| `Option.DiffMode` | `DiffMode` | `Serial` | 执行模式。`Serial` 会让默认下载编排器串行下载;`Parallel` 允许按 `Option.MaxConcurrency` 并发下载。 | -| `Option.Encoding` | `Encoding` | `UTF8` | 压缩包处理编码。 | -| `Option.Format` | `Format` | `Zip` | 更新包格式。 | -| `Option.DownloadTimeout` | `int?` | `30` | 下载超时时间,单位秒。 | -| `Option.PatchEnabled` | `bool?` | `true` | 是否启用差分补丁处理。 | -| `Option.BackupEnabled` | `bool?` | `true` | 更新前是否备份被替换文件。 | -| `Option.Silent` | `bool` | `false` | 是否启用静默轮询更新。 | -| `Option.SilentPollIntervalMinutes` | `int` | `60` | 静默模式轮询间隔。 | -| `Option.LaunchClientAfterUpdate` | `bool` | `true` | 升级后是否启动主程序。 | -| `Option.MaxConcurrency` | `int` | `3` | 默认下载编排器最大并发数,实际值会被限制到合理范围。 | -| `Option.EnableResume` | `bool` | `true` | 是否启用断点续传。 | -| `Option.RetryCount` | `int` | `3` | 下载重试次数。 | -| `Option.VerifyChecksum` | `bool` | `true` | 是否校验下载文件 Hash。 | -| `Option.RetryInterval` | `TimeSpan` | `1s` | 下载重试间隔。 | - -如果传入 `null` 给可空选项,`SetOption` 会移除当前设置,后续读取回到默认值。 - -## 事件 API {#事件-api} - -事件适合观察更新过程,不应该承载复杂业务流程。复杂流程建议封装成 `IUpdateHooks` 或 cookbook 中的完整方案。 - -### 单个事件回调 - -单个事件回调适合在启动器链式配置中直接订阅某一个通知。Core 内部通过全局 `EventManager` 按 `EventArgs` 类型分发事件;同一类型可以注册多个回调,某个回调抛出的异常会被记录到 `GeneralTracer`,不会阻断其他回调。 - -这些回调通常可能在更新流程线程、下载任务线程或差分任务线程中触发,不会自动切回 UI 线程。WPF、Avalonia、WinUI、MAUI 等客户端需要在回调里把 UI 更新切回 Dispatcher / SynchronizationContext;耗时业务也建议投递到队列或后台任务,避免阻塞下载和差分并行度。 - -| 方法 | 参数类型 | 当前代码中的触发时机 | 关键字段 | 推荐用途 | -| --- | --- | --- | --- | --- | -| `AddListenerUpdateInfo` | `UpdateInfoEventArgs` | 标准 `Client` 策略完成版本对比后触发。没有可更新内容时也会触发一次,`Info.Code` 为 `404`、`Info.Body` 为空列表;有更新时 `Info.Body` 是需要下载的 `VersionEntry` 列表。 | `Info.Code`、`Info.Message`、`Info.Body`;`VersionEntry` 包含 `RecordId`、`Name`、`Version`、`Url`、`Hash`、`AppType`、`IsForcibly`、`UpgradeMode`、`FromVersion`、`ToVersion` 等。 | 展示更新说明、版本数量、强制更新提示,或者记录服务端返回的版本元数据。不要在这里做文件替换。 | -| `AddListenerUpdatePrecheck` | `Func` | `UpdateInfo` 事件之后、Hook 和下载之前执行。按当前 `ClientStrategy.CanSkip` 实现:非强制更新下返回 `true` 表示跳过本次更新,返回 `false` 表示继续;强制更新不会进入跳过判断。 | 入参同 `UpdateInfoEventArgs`,可以读取本次更新涉及的所有 `VersionEntry`,包括版本号、更新说明、Hash、包地址、升级模式、跨版本范围等。 | 做下载前的轻量决策,也可以整理版本信息弹窗给用户,让用户阅读更新内容后决定是否继续。需要异步、可取消或有副作用的流程请使用 `IUpdateHooks.OnBeforeUpdateAsync`。 | -| `AddListenerProgress` | `ProgressEventArgs` | 默认下载通道报告 `DownloadProgress` 时触发;差分 Clean / Dirty 管道报告 `DiffProgress` 时也会触发。同一个事件参数中 `Progress` 和 `DiffProgress` 只会有一个非空。 | 下载:`Progress.AssetName`、`BytesDownloaded`、`TotalBytes`、`Percentage`、`Status`。差分:`DiffProgress.Completed`、`Total`、`CurrentFile`、`Percentage`、`IsComplete`、`Error`。 | 更新进度条、状态文本、下载速度/大小展示、差分补丁进度展示。默认下载进度应优先使用这个事件。 | -| `AddListenerMultiDownloadCompleted` | `MultiDownloadCompletedEventArgs` | `DownloadProgressReporter` 收到 `DownloadStatus.Completed` 时触发。当前默认桥接中 `Version` 实际携带 `AssetName`,自定义下载器也可以放入自己的对象。 | `Version`、`IsCompleted`。 | 标记某个资源包下载完成、追加下载日志。不要把它当作“全部资源下载完成”。 | -| `AddListenerMultiAllDownloadCompleted` | `MultiAllDownloadCompletedEventArgs` | `DefaultDownloadOrchestrator` 等待所有下载任务结束后触发一次。并发下载时它在所有任务都完成、失败结果收集完之后触发。 | `IsAllDownloadCompleted`;`FailedVersions` 是失败明细列表,元素为 `(asset, errorMessage)`。 | 在所有资源下载结束后刷新整体 UI、输出失败汇总、决定是否展示重试入口。 | -| `AddListenerMultiDownloadError` | `MultiDownloadErrorEventArgs` | `DownloadProgressReporter` 收到 `DownloadStatus.Failed` 时触发。当前默认桥接中的 `Version` 可能是 `AssetName`。 | `Exception`、`Version`。 | 记录单个资源下载失败、展示失败项、触发外部监控。整体是否成功仍以 `MultiAllDownloadCompleted` 为准。 | -| `AddListenerMultiDownloadStatistics` | `MultiDownloadStatisticsEventArgs` | 兼容旧下载统计或自定义下载实现的事件。当前默认下载编排主要通过 `AddListenerProgress` 分发下载进度,不会额外合成统计事件。 | `Version`、`Remaining`、`Speed`、`TotalBytesToReceive`、`BytesReceived`、`ProgressPercentage`。 | 如果你接入了仍会分发该事件的下载器,可以用它展示剩余时间和速度;新代码建议优先监听 `AddListenerProgress`。 | -| `AddListenerException` | `ExceptionEventArgs` | `GeneralUpdateBootstrap`、各平台策略、标准策略、OSS 策略和更新策略捕获异常时触发。 | `Exception`、`Message`。 | 上报异常、展示错误信息、写入业务日志。这个事件表示异常已经被 Core 捕获并通知,不等同于自动重试。 | - -`UpdateInfoEventArgs.Info.Body` 中的元素是 Core 经过版本对比、应用类型筛选和下载计划构建后需要处理的版本包,不是简单的原始 HTTP 响应透传。需要关注下载 URL、Hash、强制更新、跨版本差分范围时,可以直接读取 `VersionEntry` 上的属性。 - -`AddListenerUpdatePrecheck` 的返回值容易误解:以当前代码为准,返回 `true` 是“可以跳过”,不是“继续下载”。它适合放在“下载前确认”这个场景里:先从 `UpdateInfoEventArgs.Info.Body` 整理本次更新涉及的版本号、更新日志、包大小、升级类型等内容,弹窗给用户阅读;用户确认更新时返回 `false` 继续,用户选择稍后、磁盘不足或当前网络不允许时返回 `true` 跳过非强制更新。如果只是展示服务端版本信息、不需要决定是否跳过,可以只监听 `AddListenerUpdateInfo`。 - -```csharp -await new GeneralUpdateBootstrap() - .SetConfig(request) - .AddListenerUpdateInfo((_, e) => - { - Console.WriteLine($"Versions from server: {e.Info?.Body?.Count ?? 0}"); - }) - .AddListenerUpdatePrecheck(e => - { - var versions = e.Info?.Body; - var hasUpdate = (versions?.Count ?? 0) > 0; - var enoughDisk = DriveInfo.GetDrives() - .Where(d => d.IsReady) - .Any(d => d.AvailableFreeSpace > 1024L * 1024 * 1024); - var userRejected = versions != null && !ShowUpdateDialog(versions); - - // 当前实现中返回 true 表示跳过非强制更新,返回 false 表示继续。 - return !hasUpdate || !enoughDisk || userRejected; - }) - .AddListenerMultiDownloadCompleted((_, e) => - { - Console.WriteLine($"{e.Version}: {(e.IsCompleted ? "completed" : "failed")}"); - }) - .AddListenerMultiAllDownloadCompleted((_, e) => - { - Console.WriteLine(e.IsAllDownloadCompleted - ? "All downloads completed." - : $"Failed downloads: {e.FailedVersions.Count}"); - }) - .AddListenerMultiDownloadError((_, e) => - { - Console.WriteLine($"Download failed: {e.Version}"); - Console.WriteLine(e.Exception); - }) - .AddListenerProgress((_, e) => - { - if (e.Progress != null) - Console.WriteLine($"Download {e.Progress.AssetName}: {e.Progress.Percentage:F1}% {e.Progress.Status}"); - - if (e.DiffProgress != null) - Console.WriteLine($"Patch: {e.DiffProgress.Completed}/{e.DiffProgress.Total} {e.DiffProgress.CurrentFile}"); - }) - .AddListenerException((_, e) => - { - Console.WriteLine(e.Message); - Console.WriteLine(e.Exception); - }) - .LaunchAsync(); -``` - -### 批量事件监听器 - -实现 `IUpdateEventListener` 可以把事件处理集中到一个类。若只关心部分事件,继承 `UpdateEventListenerBase` 更简单。 - -```csharp -using GeneralUpdate.Core.Download; -using GeneralUpdate.Core.Event; - -public sealed class ConsoleUpdateListener : UpdateEventListenerBase -{ - public override void OnUpdateInfo(UpdateInfoEventArgs args) - { - Console.WriteLine($"Update count: {args.Info?.Body?.Count ?? 0}"); - } - - public override void OnProgress(ProgressEventArgs args) - { - if (args.Progress != null) - Console.WriteLine($"{args.Progress.AssetName}: {args.Progress.Percentage:F1}%"); - } - - public override void OnException(ExceptionEventArgs args) - { - Console.WriteLine(args.Exception); - } -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .AddEventListener() - .LaunchAsync(); -``` - -## 日志与性能 {#logging-performance} - -Core 内置 `GeneralTracer`,默认开启。它基于 `System.Diagnostics.Trace` 输出日志:Windows 下会写入调试输出窗口,同时写到控制台,并在应用基目录创建 `Logs/generalupdate-trace yyyy-MM-dd.log` 文件。文件监听器使用后台队列写入,但每条日志仍会做开关检查、时间格式化、调用栈定位和入队/输出,因此在性能敏感场景可以关闭。 - -| API | 作用 | -| --- | --- | -| `GeneralTracer.SetTracingEnabled(false)` | 关闭 Core 日志输出。关闭后 `Debug` / `Info` / `Warn` / `Error` / `Fatal` 会快速返回,同时 Trace listener 被过滤。 | -| `GeneralTracer.SetTracingEnabled(true)` | 重新开启日志输出。适合诊断版本、灰度排查或用户反馈问题时启用。 | -| `GeneralTracer.IsTracingEnabled()` | 查询当前日志开关状态。 | -| `GeneralTracer.Dispose()` | 释放文件监听器并清空 Trace listeners,通常只在测试、工具进程退出或你明确接管 Trace listeners 时使用。 | - -建议在应用启动早期设置日志开关,确保更新流程中的下载、校验、差分和替换日志都遵循同一个策略。 - -```csharp -using GeneralUpdate.Core; - -if (performanceMode) -{ - GeneralTracer.SetTracingEnabled(false); -} - -await new GeneralUpdateBootstrap() - .SetSource( - updateUrl: "https://update.example.com/api/upgrade/verification", - appSecretKey: "your-app-secret") - .SetOption(Option.AppType, AppType.Client) - .LaunchAsync(); -``` - -关闭日志适合低功耗设备、I/O 较慢的终端、静默后台轮询、批量自动化更新或对启动耗时极敏感的产品形态。排查线上问题时建议临时开启,因为 Core 的日志会记录策略分派、下载编排、校验、差分、Hook 和异常路径。 - -## 扩展点总览 - -扩展点由 `AbstractBootstrap` 提供,所有注册方法都返回当前 bootstrap,可链式调用。 - -| 注册方法 | 接口 | 影响范围 | -| --- | --- | --- | -| `Hooks()` | `IUpdateHooks` | 更新生命周期前后置逻辑。 | -| `UpdateReporter()` | `IUpdateReporter` | 更新状态上报。 | -| `SslPolicy()` | `ISslValidationPolicy` | HTTPS 证书校验。 | -| `HttpAuth()` | `IHttpAuthProvider` | HTTP 请求认证。 | -| `DownloadSource()` | `IDownloadSource` | 版本清单和下载资源来源。 | -| `DownloadPolicy()` | `IDownloadPolicy` | 下载重试、超时、熔断等策略。 | -| `DownloadExecutor()` | `IDownloadExecutor` | 单文件下载实现。 | -| `DownloadPipeline()` | `IDownloadPipeline` | 下载后处理,例如校验、解密、扫描。 | -| `DownloadOrchestrator()` | `IDownloadOrchestrator` | 批量下载完整编排。 | -| `Strategy()` | `IStrategy` | 自定义平台级更新策略。 | - -> 通过这些方法注册的类型必须有无参构造函数,因为 Core 使用 `new()` 或反射创建实例。需要复杂依赖时,建议在自定义类型内部读取配置,或在应用层封装一个无参适配器。 - -## 生命周期钩子:IUpdateHooks - -`IUpdateHooks` 适合处理“更新前检查、下载完成后处理、更新完成后清理、启动应用前准备、异常处理”等业务逻辑。它也是一个非常灵活的开放点:在 Linux 或 macOS 上,更新后的可执行文件可能需要重新赋予执行权限,或者需要先执行企业内部的授权脚本、签名校验脚本、权限修复脚本,再启动主程序;这些操作都可以放在 `OnBeforeStartAppAsync` 中完成。 - -```csharp -using GeneralUpdate.Core.Hooks; - -public sealed class ProductUpdateHooks : IUpdateHooks -{ - public Task OnBeforeUpdateAsync(HookContext ctx) - { - Console.WriteLine($"Before update: {ctx.CurrentVersion} -> {ctx.TargetVersion}"); - return Task.FromResult(true); - } - - public Task OnDownloadCompletedAsync(DownloadContext ctx) - { - Console.WriteLine($"Downloaded {ctx.AssetName}, success={ctx.Success}, path={ctx.LocalPath}"); - return Task.CompletedTask; - } - - public Task OnAfterUpdateAsync(HookContext ctx) - { - File.WriteAllText(Path.Combine(ctx.InstallPath, "last-update.txt"), DateTimeOffset.Now.ToString("O")); - return Task.CompletedTask; - } - - public Task OnUpdateErrorAsync(HookContext ctx, Exception ex) - { - File.AppendAllText(Path.Combine(ctx.InstallPath, "update-error.log"), ex + Environment.NewLine); - return Task.CompletedTask; - } - - public Task OnBeforeStartAppAsync(HookContext ctx) - { - Console.WriteLine($"Starting app from {ctx.InstallPath}"); - return Task.CompletedTask; - } -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .Hooks() - .LaunchAsync(); -``` - -Linux/macOS 场景可以直接注册内置的 `UnixPermissionHooks`,让 Core 在启动应用前执行 `chmod +x`: - -```csharp -await new GeneralUpdateBootstrap() - .SetConfig(request) - .Hooks() - .LaunchAsync(); -``` - -如果需要执行自己的赋权脚本,可以封装一个无参 hook 适配器,再通过 `Hooks()` 注册: - -```csharp -using GeneralUpdate.Core.Hooks; - -public sealed class ProductPermissionHooks : IUpdateHooks -{ - private readonly CustomPermissionHooks _inner = - new("/opt/my-product/scripts/fix-permissions.sh"); - - public Task OnBeforeStartAppAsync(HookContext ctx) - => _inner.OnBeforeStartAppAsync(ctx); - - public Task OnBeforeUpdateAsync(HookContext ctx) => Task.FromResult(true); - public Task OnDownloadCompletedAsync(DownloadContext ctx) => Task.CompletedTask; - public Task OnAfterUpdateAsync(HookContext ctx) => Task.CompletedTask; - public Task OnUpdateErrorAsync(HookContext ctx, Exception ex) => Task.CompletedTask; -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .Hooks() - .LaunchAsync(); -``` - -内置实现包括: - -| 类型 | 说明 | -| --- | --- | -| `NoOpUpdateHooks` | 默认空实现。 | -| `UnixPermissionHooks` | 在 Unix-like 系统启动前执行 `chmod +x`。 | -| `CustomPermissionHooks` | 执行自定义权限脚本;该类型构造函数需要参数,不适合直接用 `Hooks()` 注册,可自行包装无参适配器。 | - -## 状态上报:IUpdateReporter - -`IUpdateReporter` 用于把更新状态上报到服务端。 - -```csharp -using GeneralUpdate.Core.Download.Reporting; - -public sealed class ConsoleUpdateReporter : IUpdateReporter -{ - public Task ReportAsync(UpdateReport report, CancellationToken token = default) - { - Console.WriteLine($"Report: record={report.RecordId}, status={report.Status}, type={report.Type}"); - return Task.CompletedTask; - } -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .UpdateReporter() - .LaunchAsync(); -``` - -内置 `HttpUpdateReporter` 会向 `ReportUrl` 发送 JSON: - -```json -{ - "recordId": 123, - "status": 1, - "type": 1 -} -``` - -状态值: - -| 枚举 | 值 | 说明 | -| --- | --- | --- | -| `UpdateStatus.Updating` | `1` | 更新中。 | -| `UpdateStatus.Success` | `2` | 更新成功。 | -| `UpdateStatus.Failure` | `3` | 更新失败。 | - -## HTTP 认证:IHttpAuthProvider - -`IHttpAuthProvider` 可以为 Core 发出的 HTTP 请求追加认证头。 - -```csharp -using GeneralUpdate.Core.Security; - -public sealed class StaticBearerAuthProvider : IHttpAuthProvider -{ - public Task ApplyAuthAsync(HttpRequestMessage request, CancellationToken token = default) - { - request.Headers.Authorization = - new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "access-token"); - - return Task.CompletedTask; - } -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .HttpAuth() - .LaunchAsync(); -``` - -Core 内置的认证类型包括 `NoOpAuthProvider`、`BearerTokenAuthProvider`、`ApiKeyAuthProvider` 和 `HmacAuthProvider`。这些类型中部分构造函数需要参数,因此如果要通过 `HttpAuth()` 注册,通常需要写一个无参包装类。 - -## HTTPS 证书策略:ISslValidationPolicy - -`ISslValidationPolicy` 用于控制 HTTPS 证书校验。默认 `StrictSslValidationPolicy` 只接受没有 SSL policy errors 的证书。 - -```csharp -using System.Net.Security; -using System.Security.Cryptography.X509Certificates; -using GeneralUpdate.Core.Security; - -public sealed class DevelopmentSslPolicy : ISslValidationPolicy -{ - public bool ValidateCertificate( - X509Certificate2? certificate, - X509Chain? chain, - SslPolicyErrors sslPolicyErrors) - { - return sslPolicyErrors == SslPolicyErrors.None - || certificate?.Issuer.Contains("CN=Local Dev Root") == true; - } -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .SslPolicy() - .LaunchAsync(); -``` - -生产环境不建议无条件返回 `true`,否则会绕过 HTTPS 的安全保证。 - -## 下载来源:IDownloadSource - -`IDownloadSource` 负责返回待下载资源列表。适合接入私有服务、文件服务器、配置中心或自定义云存储。 - -```csharp -using GeneralUpdate.Core.Download.Abstractions; -using GeneralUpdate.Core.Download.Models; - -public sealed class StaticDownloadSource : IDownloadSource -{ - public Task ListAsync(CancellationToken token = default) - { - var assets = new[] - { - new DownloadAsset( - Name: "app-1.0.1.zip", - Url: "https://cdn.example.com/releases/app-1.0.1.zip", - Size: 25_000_000, - SHA256: "expected-sha256", - Version: "1.0.1") - }; - - return Task.FromResult(new DownloadSourceResult - { - Assets = assets, - HasMainUpdate = true, - HasUpgradeUpdate = false - }); - } -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .DownloadSource() - .LaunchAsync(); -``` - -## 下载重试策略:IDownloadPolicy - -`IDownloadPolicy` 包装单次下载动作,适合实现重试、超时、熔断或限流。 - -```csharp -using GeneralUpdate.Core.Download.Abstractions; - -public sealed class TwoAttemptDownloadPolicy : IDownloadPolicy -{ - public async Task ExecuteAsync( - Func> action, - CancellationToken token = default) - { - try - { - return await action(token); - } - catch when (!token.IsCancellationRequested) - { - await Task.Delay(TimeSpan.FromSeconds(2), token); - return await action(token); - } - } -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .DownloadPolicy() - .LaunchAsync(); -``` - -如果同时注册 `DownloadOrchestrator()`,自定义 orchestrator 会接管完整下载流程,`DownloadPolicy()` 是否生效取决于 orchestrator 自己是否使用该策略。 - -## 单文件下载:IDownloadExecutor - -`IDownloadExecutor` 负责把一个 `DownloadAsset` 下载到目标路径。适合支持 FTP、SFTP、私有协议或自定义 HTTP 客户端。 - -```csharp -using GeneralUpdate.Core.Download.Abstractions; -using GeneralUpdate.Core.Download.Models; - -public sealed class MirrorDownloadExecutor : IDownloadExecutor -{ - private readonly HttpClient _client = new(); - - public async Task ExecuteAsync( - DownloadAsset asset, - string destPath, - IProgress? progress = null, - CancellationToken token = default) - { - var started = DateTimeOffset.Now; - await using var input = await _client.GetStreamAsync(asset.Url, token); - await using var output = File.Create(destPath); - await input.CopyToAsync(output, token); - - var fileInfo = new FileInfo(destPath); - return new DownloadResult(asset, destPath, fileInfo.Length, DateTimeOffset.Now - started, 0, true, null); - } -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .DownloadExecutor() - .LaunchAsync(); -``` - -## 下载后处理:IDownloadPipeline - -`IDownloadPipeline` 在文件下载完成后运行。适合做 Hash 校验、解密、病毒扫描、格式转换等。 - -```csharp -using GeneralUpdate.Core.Download.Abstractions; - -public sealed class AntivirusPipeline : IDownloadPipeline -{ - public Task ProcessAsync(string downloadedPath, CancellationToken token = default) - { - if (!File.Exists(downloadedPath)) - throw new FileNotFoundException("Downloaded file not found.", downloadedPath); - - Console.WriteLine($"Scanning {downloadedPath}"); - return Task.FromResult(downloadedPath); - } -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .DownloadPipeline() - .LaunchAsync(); -``` - -Core 在创建下载管道时会优先尝试使用 `string` 构造函数传入期望 Hash;如果没有该构造函数,则使用无参构造函数。 - -## 批量下载编排:IDownloadOrchestrator - -`IDownloadOrchestrator` 是下载子系统的最高层扩展点。注册后,它会接管批量下载、并发控制、重试、进度和结果汇总。 - -```csharp -using GeneralUpdate.Core.Download.Abstractions; -using GeneralUpdate.Core.Download.Executors; -using GeneralUpdate.Core.Download.Models; - -public sealed class SerialDownloadOrchestrator : IDownloadOrchestrator -{ - private readonly IDownloadExecutor _executor = new HttpDownloadExecutor(new HttpClient()); - - public async Task ExecuteAsync( - DownloadPlan plan, - string destDir, - int maxConcurrency = 3, - IProgress? progress = null, - CancellationToken token = default) - { - var results = new List(); - var started = DateTimeOffset.Now; - - foreach (var asset in plan.Assets) - { - var destPath = Path.Combine(destDir, asset.Name); - results.Add(await _executor.ExecuteAsync(asset, destPath, progress, token)); - } - - return new DownloadReport( - results, - results.Where(r => r.Success).Sum(r => r.DownloadedBytes), - DateTimeOffset.Now - started, - results.Count(r => r.Success), - results.Count(r => !r.Success)); - } -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .DownloadOrchestrator() - .LaunchAsync(); -``` - -只有当你需要完整替换下载行为时才建议实现 orchestrator。多数情况下替换 `IDownloadExecutor`、`IDownloadPolicy` 或 `IDownloadPipeline` 就够了。 - -## 平台策略:IStrategy - -`IStrategy` 是最高级别的更新策略接口。Core 内置 `ClientStrategy`、`UpdateStrategy`、`OssStrategy` 以及 Windows/Linux/macOS 平台策略。只有在你需要替换平台级文件操作或启动逻辑时,才应实现它。 - -```csharp -using GeneralUpdate.Core.Configuration; -using GeneralUpdate.Core.Download.Reporting; -using GeneralUpdate.Core.Hooks; -using GeneralUpdate.Core.Strategy; - -public sealed class LoggingStrategy : IStrategy -{ - private UpdateContext? _context; - - public IUpdateHooks Hooks { get; set; } = new NoOpUpdateHooks(); - public IUpdateReporter Reporter { get; set; } = new HttpUpdateReporter(); - - public void Create(UpdateContext parameter) - { - _context = parameter; - } - - public async Task ExecuteAsync() - { - if (_context == null) - throw new InvalidOperationException("Strategy was not initialized."); - - Console.WriteLine($"Custom strategy executing in {_context.InstallPath}"); - await Hooks.OnBeforeUpdateAsync(new HookContext( - _context.UpdateAppName, - _context.InstallPath, - _context.ClientVersion, - _context.LastVersion, - _context.AppType ?? AppType.Client)); - } - - public Task StartAppAsync() - { - Console.WriteLine("Custom start app logic."); - return Task.CompletedTask; - } -} - -await new GeneralUpdateBootstrap() - .SetConfig(request) - .Strategy() - .LaunchAsync(); -``` - -> 静默更新不是单独的扩展接口,而是内置执行策略。配置方式和生命周期见 [静默更新策略](#silent-update-strategy)。 - -## 与 GeneralUpdate.Tools 的关系 - -Core 消费更新清单和更新包;`GeneralUpdate.Tools` 负责辅助生成和验证这些产物。 - -| Tools 能力 | Core 中对应消费点 | -| --- | --- | -| Patch Package | `Option.PatchEnabled`、`UseDiffPipeline`、差分补丁处理。 | -| Manifest Generator | `ManifestInfo`、`AppMetadataDiscoverer`、版本回写。 | -| Extension Package | 作为更新包内容或扩展包分发,由下载和部署流程消费。 | -| OSS Config | `OssClient` / `OssUpgrade` 角色读取 OSS 配置并下载。 | -| Hash / Simulation / Report | 对应 `Option.VerifyChecksum`、下载后校验和状态上报。 | - -## 相关示例 - -- [Upgrade sample](https://github.com/GeneralLibrary/GeneralUpdate-Samples/blob/main/src/Upgrade/Program.cs) -- [OSS upgrade sample](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/OSS/OSSUpgradeSample) -- [GeneralUpdate repository](https://github.com/GeneralLibrary/GeneralUpdate) -- [GeneralUpdate.Tools repository](https://github.com/GeneralLibrary/GeneralUpdate.Tools) diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Differential.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Differential.md deleted file mode 100644 index 60c72b8..0000000 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Differential.md +++ /dev/null @@ -1,442 +0,0 @@ ---- -sidebar_position: 6 ---- - -# GeneralUpdate.Differential - -**命名空间:** `GeneralUpdate.Differential`、`GeneralUpdate.Differential.Differ`、`GeneralUpdate.Differential.Abstractions` | **主要入口:** `IBinaryDiffer`、`BsdiffDiffer`、`StreamingHdiffDiffer` | **NuGet 包:** `GeneralUpdate.Differential` - -`GeneralUpdate.Differential` 是 GeneralUpdate 的二进制差分组件,专注解决”一个旧文件 + 一个补丁文件 = 一个新文件”的问题。它提供可替换的文件级差分算法、补丁压缩抽象和 BSDIFF 兼容补丁读写能力;目录级对比、批量补丁生成、并行调度、删除文件处理和更新流程编排由 `GeneralUpdate.Core` 的 `DiffPipeline` 或 `GeneralUpdate.Tools` 承担。 - -## 文档大纲与知识点导航 {#knowledge-map} - -如果你第一次阅读 Differential 文档,可以先看这个导航,再跳到对应知识点。本文按照“能力边界 -> 文件级 API -> 算法选择 -> 压缩格式 -> 与 Core/Tools 集成 -> 性能与扩展”的顺序组织。 - -| 你想了解什么 | 推荐阅读 | -| --- | --- | -| Differential 到底负责什么、不负责什么 | [组件能力边界](#组件能力边界) | -| `Clean` / `Dirty` 是什么含义 | [Clean 与 Dirty 语义](#clean-与-dirty-语义) | -| 如何给单个文件生成并应用补丁 | [单文件快速开始](#单文件快速开始) | -| 使用 Core 时是否还要手动集成 Differential | [与 GeneralUpdate.Core 的关系](#与-generalupdatecore-的关系) | -| 当前有哪些差分算法,如何选择 | [差分算法选择](#差分算法选择) | -| BSDIFF 补丁格式和压缩字节怎么工作 | [补丁格式与压缩 Provider](#补丁格式与压缩-provider) | -| 如何在 Core 更新流程里启用目录级差分 | [与 GeneralUpdate.Core 的关系](#与-generalupdatecore-的关系) | -| Tools 构建差分包时用了什么能力 | [与 GeneralUpdate.Tools 的关系](#与-generalupdatetools-的关系) | -| 下载和差分是否可以多线程并行 | [并发模型与性能建议](#并发模型与性能建议) | -| 大型项目如何提升差分构建效率 | [大型项目并行差分](#大型项目并行差分) | -| 如何接入自定义差分算法或压缩方式 | [扩展点](#扩展点) | - -## 组件能力边界 - -Differential 是底层文件补丁库,不是完整的更新编排器。理解这个边界可以避免把旧文档里的 `DifferentialCore`、黑名单、目录批量处理等概念误认为当前组件 API。 - -| 能力 | Differential 是否负责 | 说明 | -| --- | --- | --- | -| 单文件二进制补丁生成 | 是 | 通过 `IBinaryDiffer.CleanAsync(oldFile, newFile, patchFile)` 完成。 | -| 单文件二进制补丁应用 | 是 | 通过 `IBinaryDiffer.DirtyAsync(oldFile, outputNewFile, patchFile)` 完成。 | -| 差分算法实现 | 是 | 当前主要实现为 `BsdiffDiffer` 和 `StreamingHdiffDiffer`。 | -| 补丁数据压缩/解压 | 是 | 通过 `ICompressionProvider` 抽象,内置 BZip2、Deflate,源码中预留 .NET 6+ Brotli。 | -| 目录级新旧版本对比 | 否 | 由 `GeneralUpdate.Core.Pipeline.DiffPipeline` 的 matcher 负责。 | -| 新增文件复制、删除清单、批量 patch 命名 | 否 | 由 `DiffPipeline` 负责生成 `.patch` 文件、复制新增文件和写入 `generalupdate.delete.json`。 | -| 更新包生成工具 | 否 | 推荐由 `GeneralUpdate.Tools` 调用 Core 差分管道生成发布产物。 | -| 下载、校验、解压、版本回写、重启 | 否 | 这些属于 `GeneralUpdate.Core` 更新流程。 | - -> 当前源码中没有旧文档提到的 `DifferentialCore` 单例。直接使用 Differential 组件时,请面向 `IBinaryDiffer` 和具体 differ 实现编程;需要目录级能力时使用 Core 的 `DiffPipeline`。 - -## Clean 与 Dirty 语义 {#clean-与-dirty-语义} - -Differential 沿用了 GeneralUpdate 差分流程中的两个术语: - -| 术语 | 方法 | 输入 | 输出 | 常用位置 | -| --- | --- | --- | --- | --- | -| `Clean` | `CleanAsync` | 旧文件、新文件、补丁路径 | `.patch` 补丁文件 | 构建/发布阶段 | -| `Dirty` | `DirtyAsync` | 旧文件、输出新文件路径、补丁路径 | 还原后的新文件 | 客户端升级阶段 | - -文件级补丁应用不会直接覆盖旧文件,而是把还原结果写到你传入的 `newFilePath`。Core 的 `DiffPipeline` 在目录级更新时会先写临时文件,成功后再替换原文件,从而避免补丁应用失败时破坏原文件。 - -## 单文件快速开始 - -下面示例只演示 Differential 的底层单文件能力。如果你已经在使用 `GeneralUpdate.Core`,Core 默认已经集成 Differential,不需要为了正常更新流程再手动集成或直接调用本组件。如果你要比较两个目录、生成一批 `.patch`、复制新增文件或处理删除文件,请直接看 [与 GeneralUpdate.Core 的关系](#与-generalupdatecore-的关系)。 - -```csharp -using GeneralUpdate.Differential.Abstractions; -using GeneralUpdate.Differential.Differ; - -IBinaryDiffer differ = new BsdiffDiffer(); - -var oldFile = @"D:\releases\1.0.0\app.dll"; -var newFile = @"D:\releases\1.0.1\app.dll"; -var patchFile = @"D:\patches\app.dll.patch"; -var outputFile = @"D:\restore\app.dll"; - -// 生成补丁:oldFile + newFile -> patchFile -await differ.CleanAsync(oldFile, newFile, patchFile); - -// 应用补丁:oldFile + patchFile -> outputFile -await differ.DirtyAsync(oldFile, outputFile, patchFile); -``` - -`CleanAsync` 和 `DirtyAsync` 都支持 `CancellationToken`。当前实现会在任务开始和 Core 管道调度点观察取消请求;单个算法内部不是每一个字节循环都检查取消,因此大文件取消可能会等到当前文件处理结束后才完全停下。 - -## 核心 API - -### IBinaryDiffer - -`IBinaryDiffer` 是所有文件级差分算法的统一抽象,也是 Core 差分管道接入自定义算法的关键接口。 - -```csharp -public interface IBinaryDiffer -{ - Task DirtyAsync( - string oldFilePath, - string newFilePath, - string patchFilePath, - CancellationToken cancellationToken = default); - - Task CleanAsync( - string oldFilePath, - string newFilePath, - string patchFilePath, - CancellationToken cancellationToken = default); -} -``` - -| 参数 | 含义 | -| --- | --- | -| `oldFilePath` | 旧版本文件路径。生成补丁和应用补丁时都需要。 | -| `newFilePath` | `CleanAsync` 中表示新版本源文件;`DirtyAsync` 中表示还原后的输出文件。 | -| `patchFilePath` | 补丁文件路径。`CleanAsync` 写入它,`DirtyAsync` 读取它。 | - -### BsdiffDiffer - -`BsdiffDiffer` 实现 BSDIFF 4.0 文件级二进制差分算法。它会把旧文件和新文件读入内存,通过后缀排序寻找匹配块,再输出控制段、差异段和额外段。 - -```csharp -using GeneralUpdate.Differential.Differ; - -var differ = new BsdiffDiffer(); -await differ.CleanAsync(oldFile, newFile, patchFile); -await differ.DirtyAsync(oldFile, outputFile, patchFile); -``` - -| 特性 | 说明 | -| --- | --- | -| 默认压缩 | `BZip2CompressionProvider`,兼容历史 BSDIFF 补丁。 | -| 可替换压缩 | 构造函数接受 `ICompressionProvider`。 | -| 补丁兼容 | 支持 32 字节旧 BSDIFF 头,也支持 33 字节扩展头。 | -| 适用场景 | 追求兼容性、补丁体积稳定、单文件体积可控的场景。 | -| 资源特征 | 生成补丁时会读入旧文件和新文件,单文件很大时需要关注内存占用。 | - -`BsdiffDiffer` 也保留了 `Clean(...)` 和 `Dirty(...)` 方法;新代码建议优先面向 `IBinaryDiffer.CleanAsync` / `DirtyAsync`,便于切换算法。 - -### StreamingHdiffDiffer - -`StreamingHdiffDiffer` 是当前源码中的另一种 differ 实现。它使用块级 FNV-1a 哈希索引预筛候选位置,再进行字节级扩展匹配,输出 BSDIFF 兼容的补丁结构。 - -```csharp -using GeneralUpdate.Differential.Abstractions; -using GeneralUpdate.Differential.Differ; - -var differ = new StreamingHdiffDiffer( - compressionProvider: new DeflateCompressionProvider(optimalLevel: true), - blockSize: 64 * 1024, - maxWindowSize: 128 * 1024 * 1024); - -await differ.CleanAsync(oldFile, newFile, patchFile); -await differ.DirtyAsync(oldFile, outputFile, patchFile); -``` - -| 特性 | 说明 | -| --- | --- | -| 默认压缩 | `DeflateCompressionProvider`。 | -| 块大小 | `BlockSize` 默认 64 KB,用于建立旧文件块哈希索引。 | -| 窗口预算 | `MaxWindowSize` 默认 128 MB,影响生成补丁时参与计算的内存窗口。 | -| 应用补丁 | `DirtyAsync` 委托给 `BsdiffDiffer` 的补丁应用逻辑。 | -| 适用场景 | 需要更快候选匹配、希望与 Core `DiffPipeline` 默认算法保持一致的目录级差分构建。 | - -需要注意的是,当前实现不是完整外存流式差分:当单个文件超过 `MaxWindowSize` 时,算法只会读取预算窗口参与计算。对超大单文件,请在业务侧验证补丁还原结果,或调大 `MaxWindowSize`,或改用 `BsdiffDiffer` 等更适合当前文件规模的实现。 - -## 差分算法选择 - -当前 Differential 内置两种文件级差分算法。它们都输出 BSDIFF 兼容补丁结构,但生成补丁时的匹配方式、默认压缩、性能侧重点不同。 - -| 对比项 | `BsdiffDiffer` | `StreamingHdiffDiffer` | -| --- | --- | --- | -| 核心思路 | 经典 BSDIFF 4.0,基于后缀排序寻找旧文件和新文件之间的最长匹配。 | 使用块级 FNV-1a 哈希建立旧文件索引,先用哈希快速筛选候选块,再做字节级扩展匹配。 | -| 默认压缩 | BZip2 (`0x00`)。 | Deflate (`0x01`)。 | -| 补丁应用 | 自己实现 BSDIFF Dirty 逻辑。 | `DirtyAsync` 委托给 `BsdiffDiffer`,因此应用阶段和 BSDIFF 补丁兼容。 | -| 生成效率 | 匹配更精细,局部或分散变化下补丁生成表现稳定;但后缀排序和全量读入会带来 CPU/内存开销。 | 块命中效果好时生成更快;如果变化分散、块哈希命中少,生成可能变慢。 | -| 客户端应用性能 | 默认 BZip2 解压成本更高,客户端应用大量补丁时耗时可能更明显。 | 默认 Deflate 解压更快,更适合客户端批量应用补丁。 | -| 补丁体积倾向 | 通常更追求细粒度匹配,补丁体积明显更稳定。 | 速度优先,补丁体积与文件变化分布、块大小、窗口预算强相关;块命中差时可能接近完整文件。 | -| 内存特征 | 生成阶段读取旧文件和新文件,单个大文件需要关注内存峰值。 | 通过 `BlockSize` 和 `MaxWindowSize` 控制匹配窗口,超大单文件需要额外验证或调参。 | -| 兼容性 | 最适合需要兼容旧 BSDIFF/BZip2 补丁的场景。 | 适合新项目、目录级批量差分和 Core `DiffPipeline` 默认构建。 | - -可以简单理解为:`BsdiffDiffer` 更偏“兼容和补丁体积稳定”,`StreamingHdiffDiffer` 更偏“客户端应用速度和可调参数”。如果项目非常在意补丁体积或文件变化较分散,优先考虑 `BsdiffDiffer`;如果项目更在意客户端应用速度,并且经过压测确认补丁体积可接受,可以考虑 `StreamingHdiffDiffer`。 - -### 参考基准数据 {#benchmark-reference} - -下面数据来自当前源码的一组本地微基准,用于给开发者判断量级,不是跨所有项目的性能承诺。测试环境为 Windows x64、.NET Release 构建,使用 2-4 MB 合成文件;真实结果会受 CPU、磁盘、文件类型、变化比例、压缩级别和并行度影响。 - -| 场景 | `BsdiffDiffer` 生成 | `StreamingHdiffDiffer` 生成 | `BsdiffDiffer` 应用 | `StreamingHdiffDiffer` 应用 | `BsdiffDiffer` 补丁体积 | `StreamingHdiffDiffer` 补丁体积 | -| --- | ---: | ---: | ---: | ---: | ---: | ---: | -| 2 MB 文本,少量行变更/插入 | 484 ms | 2059 ms | 55 ms | 36 ms | 0.05% | 3.50% | -| 4 MB 二进制,连续局部块变更 | 1030 ms | 318 ms | 55 ms | 11 ms | 2.58% | 100.04% | -| 4 MB 二进制,随机分散字节变更 | 757 ms | 4176 ms | 70 ms | 30 ms | 2.18% | 100.27% | - -从这组数据可以得到几个实用预估: - -| 指标 | 参考结论 | -| --- | --- | -| 补丁体积 | `BsdiffDiffer` 在测试场景中约为新文件的 0.05%-2.58%;`StreamingHdiffDiffer` 约为 3.50%-100%。如果补丁包大小是第一优先级,优先测试 `BsdiffDiffer`。 | -| 客户端应用速度 | `StreamingHdiffDiffer` 默认 Deflate,在测试中应用补丁约快 1.5-5 倍。大量文件批量应用时,这个差距会更明显。 | -| 生成速度 | 没有绝对赢家:连续局部二进制变更中 `StreamingHdiffDiffer` 约快 3.2 倍;文本和随机分散变更中 `BsdiffDiffer` 约快 4.3-5.5 倍。 | -| 大型项目选择 | 大型项目建议同时看“补丁总体积 + 构建耗时 + 客户端应用耗时”。如果大量文件可以并行,`WithParallelism(...)` 往往比单个 differ 的微小差距更影响总体耗时。 | - -> 这组数据的重点是帮助判断方向:`BsdiffDiffer` 通常更容易得到小补丁,`StreamingHdiffDiffer` 的应用阶段更快,但补丁体积和生成速度对文件变化形态非常敏感。正式发布前建议用自己项目的真实产物做一次压测。 - -推荐选择: - -| 场景 | 建议 | -| --- | --- | -| 只需要低层单文件补丁,并希望最大兼容 | 使用 `new BsdiffDiffer()`。 | -| 通过 Core `DiffPipeline` 批量生成目录级补丁 | 先用默认配置跑基准;若补丁体积偏大,可显式切换到 `BsdiffDiffer`;再结合 `WithParallelism(...)` 提升吞吐。 | -| 客户端解压性能更敏感 | 优先选择 Deflate 补丁,即 `StreamingHdiffDiffer` 默认配置,或 `new BsdiffDiffer(new DeflateCompressionProvider())`。 | -| 历史补丁仍是旧 BSDIFF/BZip2 | 使用 `BsdiffDiffer` 应用;32 字节头会按 BZip2 处理。 | -| 大型项目包含大量 DLL、资源文件、插件文件 | 使用 Core `DiffPipeline` 做文件级并行,避免自己逐个文件串行调用 Differential。 | - -## 补丁格式与压缩 Provider {#补丁格式与压缩-provider} - -Differential 生成的是 BSDIFF 风格补丁。当前实现写入 33 字节扩展头: - -| 偏移 | 长度 | 含义 | -| --- | --- | --- | -| `0` | 8 | 魔数 `"BSDIFF40"`。 | -| `8` | 8 | 压缩后控制段长度。 | -| `16` | 8 | 压缩后差异段长度。 | -| `24` | 8 | 新文件长度。 | -| `32` | 1 | 压缩格式版本。 | - -应用补丁时也兼容 32 字节旧头:如果没有第 33 个格式字节,就按 BZip2 旧补丁处理。 - -### ICompressionProvider - -`ICompressionProvider` 负责把控制段、差异段和额外段包装成压缩流。 - -```csharp -public interface ICompressionProvider -{ - byte FormatVersion { get; } - - Stream CreateCompressStream( - Stream output, - CancellationToken cancellationToken = default); - - Stream CreateDecompressStream( - Stream input, - CancellationToken cancellationToken = default); -} -``` - -| Provider | 格式字节 | 当前可用性 | 说明 | -| --- | --- | --- | --- | -| `BZip2CompressionProvider` | `0x00` | 可用 | `BsdiffDiffer` 默认值,兼容旧 BSDIFF 补丁。 | -| `DeflateCompressionProvider` | `0x01` | 可用 | BCL `DeflateStream`,解压速度更适合客户端更新。 | -| `BrotliCompressionProvider` | `0x02` | 源码中以 `NET6_0_OR_GREATER` 条件编译预留 | 当前 `GeneralUpdate.Differential` 项目目标为 `netstandard2.0`,并且补丁读取逻辑当前只识别 `0x00` / `0x01`,不要把 Brotli 作为生产更新包格式。 | - -自定义压缩时,生成补丁和应用补丁必须使用能被补丁读取逻辑识别的格式字节。当前生产建议只使用 BZip2 或 Deflate。 - -```csharp -using GeneralUpdate.Differential.Abstractions; -using GeneralUpdate.Differential.Differ; - -var differ = new BsdiffDiffer( - new DeflateCompressionProvider(optimalLevel: false)); - -await differ.CleanAsync(oldFile, newFile, patchFile); -await differ.DirtyAsync(oldFile, outputFile, patchFile); -``` - -## 与 GeneralUpdate.Core 的关系 {#与-generalupdatecore-的关系} - -`GeneralUpdate.Core` 在 Differential 之上提供目录级差分管道 `DiffPipeline`。它会负责: - -1. 对比旧目录和新目录。 -2. 找出发生变化的文件并调用 `IBinaryDiffer.CleanAsync` 生成 `.patch`。 -3. 复制新增文件到补丁目录。 -4. 生成 `generalupdate.delete.json` 记录删除文件。 -5. 客户端应用补丁时并行调用 `IBinaryDiffer.DirtyAsync`,先写临时文件,成功后替换原文件。 - -如果你在应用更新流程中使用 `GeneralUpdate.Core`,Core 默认已经集成 Differential 并内置差分管道。也就是说,常规更新接入时不需要额外安装、初始化或手动调用 `GeneralUpdate.Differential`;只要使用 Core 的更新流程,并按业务需要启用补丁更新能力,Core 会在内部完成 differ 创建、补丁应用和目录级编排。 - -只有在你想替换默认差分算法、调整并行度、改变错误策略或接入自定义 matcher 时,才需要通过 `UseDiffPipeline` 做高级配置: - -```csharp -using GeneralUpdate.Core; -using GeneralUpdate.Core.Models; -using GeneralUpdate.Differential.Differ; - -await new GeneralUpdateBootstrap() - .SetSource( - updateUrl: "https://update.example.com/api/upgrade/verification", - appSecretKey: "your-app-secret") - .SetOption(Option.AppType, AppType.Client) - .SetOption(Option.PatchEnabled, true) - .UseDiffPipeline(builder => builder - .UseDiffer(new StreamingHdiffDiffer()) - .WithParallelism(4) - .WithStopOnFirstError(true)) - .LaunchAsync(); -``` - -当前源码里有两个默认层级需要区分: - -| 使用方式 | 默认 differ | -| --- | --- | -| 直接 `new DiffPipeline()` 或 `new DiffPipelineBuilder().Build()` | `StreamingHdiffDiffer` | -| `GeneralUpdateBootstrap` 未显式调用 `UseDiffPipeline(...)` 时内部构建 | `BsdiffDiffer`,并行度 2,带 `DiffProgressReporter` | - -因此,普通用户可以把 Differential 看作 Core 已经带好的底层能力,不需要特地集成;只有希望 Core 更新流程明确使用某个算法或自定义差分行为时,才建议显式调用 `UseDiffPipeline(...)`。 - -## 与 GeneralUpdate.Tools 的关系 {#与-generalupdatetools-的关系} - -`GeneralUpdate.Tools` 面向发布侧,帮助开发者构建更新产物。当前 `DiffService` 会创建 `new DiffPipeline()`,再调用: - -```csharp -await pipeline.CleanAsync(oldDir, newDir, patchDir); -``` - -也就是说,Tools 生成目录级差分包时,本质上使用的是 Core 的 `DiffPipeline`,而 `DiffPipeline` 再调用 Differential 的 `IBinaryDiffer` 生成每个变更文件的补丁。对大多数开发者来说,推荐路径是: - -1. 用 Tools 对比旧版本目录和新版本目录,生成补丁目录和清单产物。 -2. 用 Core 在客户端检查版本、下载补丁包、应用补丁。 -3. 只有在需要自定义差分算法、压缩格式或单文件补丁实验时,才直接使用 Differential。 - -这种分层可以让业务代码保持简单:Tools 负责构建,Core 负责更新,Differential 负责底层文件差分。 - -## 并发模型与性能建议 - -Differential 的单个 differ 实例没有保存某次补丁任务的可变共享状态。只要传入的 `ICompressionProvider` 是线程安全的,内置 differ 可以被 Core 管道并发调用;内置 BZip2、Deflate provider 都会为每次调用创建新的压缩流,适合并发使用。 - -真正的“多线程差分”通常发生在 Core `DiffPipeline` 层: - -```csharp -var pipeline = new DiffPipelineBuilder() - .UseDiffer(new StreamingHdiffDiffer()) - .WithParallelism(4) - .Build(); - -await pipeline.CleanAsync(oldDir, newDir, patchDir); -``` - -### 大型项目并行差分 {#大型项目并行差分} - -大型桌面项目通常不是“一个超大文件”,而是由主程序、多个 DLL、插件、资源文件、运行时文件和配置文件组成。Core `DiffPipeline` 会把目录对比结果拆成文件级任务,每个变更文件独立调用 `IBinaryDiffer.CleanAsync` 生成补丁,因此可以通过 `WithParallelism(...)` 同时处理多个文件。 - -这种并行模型对大型项目很重要: - -1. 构建侧可以同时为多个变更文件生成 `.patch`,缩短发布包构建时间。 -2. 客户端应用补丁时也可以并行处理多个文件,减少升级窗口。 -3. 新增文件复制、删除清单处理和差分补丁生成由 Core 管道统一编排,开发者不需要手写多线程调度。 -4. 并行度可以按机器能力调整,构建机可以设置更高,低配置客户端可以保持较低。 - -| 参数/策略 | 建议 | -| --- | --- | -| `WithParallelism(1)` | 资源敏感、机械硬盘、低内存环境。 | -| `WithParallelism(2)` | 默认平衡值,适合多数桌面应用。 | -| `WithParallelism(4-8)` | 多核 CPU、SSD、构建机或发布服务器。 | -| BZip2 | 补丁兼容性好,但客户端解压成本更高。 | -| Deflate | 解压速度更友好,适合客户端大批量应用补丁。 | -| 大文件 | 先压测补丁生成耗时、内存峰值和还原结果,不要只看补丁体积。 | - -并行差分适合“文件数量多、每个文件可独立处理”的大型项目。需要注意的是,单个超大文件内部仍由具体 differ 算法处理,不会因为 `WithParallelism(8)` 就把一个文件拆成 8 份并行计算;并行度提升的是多个文件之间的吞吐。 - -下载与差分可以在上层更新流程中并行:Core 下载阶段可以并发拉取多个资源,差分应用阶段也可以按文件并行处理补丁。Differential 只负责单个文件的补丁计算,不直接管理网络下载线程。 - -## 扩展点 - -### 自定义差分算法 - -实现 `IBinaryDiffer` 后即可接入 Core 管道。适合接入其他算法、调用原生库,或对特定文件类型做特殊优化。 - -```csharp -using GeneralUpdate.Differential.Abstractions; - -public sealed class MyBinaryDiffer : IBinaryDiffer -{ - public Task CleanAsync( - string oldFilePath, - string newFilePath, - string patchFilePath, - CancellationToken cancellationToken = default) - { - // Generate patchFilePath from oldFilePath and newFilePath. - throw new NotImplementedException(); - } - - public Task DirtyAsync( - string oldFilePath, - string newFilePath, - string patchFilePath, - CancellationToken cancellationToken = default) - { - // Restore newFilePath from oldFilePath and patchFilePath. - throw new NotImplementedException(); - } -} -``` - -```csharp -var pipeline = new DiffPipelineBuilder() - .UseDiffer(new MyBinaryDiffer()) - .WithParallelism(4) - .Build(); -``` - -自定义算法需要保证 `CleanAsync` 产出的补丁能被同一算法的 `DirtyAsync` 正确应用;如果补丁要交给 Core 客户端使用,发布侧和客户端必须使用同一套 differ 实现。 - -### 自定义压缩 Provider - -如果仍使用 BSDIFF 兼容补丁结构,只想替换控制段、差异段和额外段的压缩方式,可以实现 `ICompressionProvider`。 - -```csharp -using GeneralUpdate.Differential.Abstractions; - -public sealed class MyCompressionProvider : ICompressionProvider -{ - public byte FormatVersion => 0x01; - - public Stream CreateCompressStream( - Stream output, - CancellationToken cancellationToken = default) - { - return new DeflateStream( - output, - CompressionLevel.Optimal, - leaveOpen: true); - } - - public Stream CreateDecompressStream( - Stream input, - CancellationToken cancellationToken = default) - { - return new DeflateStream( - input, - CompressionMode.Decompress, - leaveOpen: true); - } -} -``` - -不要随意分配新的 `FormatVersion`。当前 `BsdiffDiffer.DirtyAsync` 只识别 BZip2 (`0x00`) 和 Deflate (`0x01`);如果你引入新格式,也需要同步扩展补丁读取逻辑,否则客户端无法应用补丁。 - -## 实战建议 - -| 场景 | 推荐做法 | -| --- | --- | -| 普通应用发布差分更新 | 使用 `GeneralUpdate.Tools` 生成产物,客户端使用 Core。 | -| 需要控制目录级并行、错误策略和进度 | 使用 Core `DiffPipelineBuilder`。 | -| 只验证某个文件的补丁效果 | 直接使用 `IBinaryDiffer`。 | -| 对补丁体积和应用速度都敏感 | 对同一组文件分别测试 BZip2、Deflate 和不同算法后再定默认策略。 | -| 更新包需要长期兼容旧客户端 | 保守使用 `BsdiffDiffer` + BZip2,或确保客户端已支持 Deflate 扩展头。 | - -Differential 的价值在于把复杂的二进制差分能力收敛成稳定的文件级抽象。上层开发者可以把重点放在“什么时候更新、下载什么、如何提示用户”上,把具体补丁生成和应用交给 Core/Tools/Differential 的组合完成。 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Drivelution.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Drivelution.md index 12b82aa..bf93c9f 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Drivelution.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Drivelution.md @@ -554,4 +554,4 @@ GeneralTracer.SetTracingEnabled(true); - [驱动更新示例](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/Hub/Samples/ImDiskQuickInstallSample.cs) - [GeneralUpdate 仓库](https://github.com/GeneralLibrary/GeneralUpdate) -- [GeneralUpdate.Drivelution 组件文档](../doc/GeneralUpdate.Drivelution.md) +- [GeneralUpdate.Drivelution 组件文档](../doc/GeneralUpdate.Drivelution) diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md index 569f5d7..81e048a 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md @@ -676,4 +676,4 @@ report-extension_1.0.0.zip - [扩展管理示例](https://github.com/GeneralLibrary/GeneralUpdate-Samples/tree/main/src/Hub/Samples/ExtensionSample.cs) - [GeneralUpdate 仓库](https://github.com/GeneralLibrary/GeneralUpdate) -- [打包指南](../guide/Packaging.md) +- [打包指南](../guide/Packaging) diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/guide/FAQ.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/guide/FAQ.md index 096cdf3..3e60d12 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/guide/FAQ.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/guide/FAQ.md @@ -332,7 +332,7 @@ await new GeneralClientBootstrap() ## 相关资源 -- **[GeneralUpdate.Core](../doc/GeneralUpdate.Core.md)** — 核心更新引擎 -- **[入门实战手册](../quickstart/Beginner%20cookbook.md)** — 从零跑通更新闭环 -- **[GeneralUpdate.Tools](../quickstart/GeneralUpdate.PacketTool.md)** — 打包工具 -- **[GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl.md)** — 崩溃监控与回滚 +- **[GeneralUpdate.Core](../doc/GeneralUpdate.Core)** — 核心更新引擎 +- **[入门实战手册](../quickstart/Beginner%20cookbook)** — 从零跑通更新闭环 +- **[GeneralUpdate.Tools](../quickstart/GeneralUpdate.PacketTool)** — 打包工具 +- **[GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl)** — 崩溃监控与回滚 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/quickstart/Beginner cookbook.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/quickstart/Beginner cookbook.md index e60f34e..b429b71 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/quickstart/Beginner cookbook.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/quickstart/Beginner cookbook.md @@ -11,12 +11,12 @@ title: 入门实战手册 | 角色 | 在样例中的位置 | 负责什么 | 深入阅读 | | --- | --- | --- | --- | -| Hub | `src\Hub` | 交互式示例浏览器,通过菜单选择并运行各类更新场景 | [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md) | -| Server | `src\Server` | 返回版本信息、接收更新报告、提供补丁下载 | [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md) | -| Packet | `src\Server\wwwroot\packages` | 可下载的 `.zip` 更新包和 `versions.json` 元数据 | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md) | -| Tools | GeneralUpdate.Tools 仓库 | 生成补丁包、Hash、OSS 清单、manifest 和仿真报告 | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md) | -| Bowl | Hub Samples 中集成 | 监控进程异常并导出失败信息 | [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl.md) | -| Differential | Hub Samples + Core 默认集成 | 对 old/new 文件生成差分,并在更新阶段应用 | [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential.md) | +| Hub | `src\Hub` | 交互式示例浏览器,通过菜单选择并运行各类更新场景 | [GeneralUpdate.Core](../doc/GeneralUpdate.Core) | +| Server | `src\Server` | 返回版本信息、接收更新报告、提供补丁下载 | [GeneralUpdate.Core](../doc/GeneralUpdate.Core) | +| Packet | `src\Server\wwwroot\packages` | 可下载的 `.zip` 更新包和 `versions.json` 元数据 | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool) | +| Tools | GeneralUpdate.Tools 仓库 | 生成补丁包、Hash、OSS 清单、manifest 和仿真报告 | [GeneralUpdate.Tools](./GeneralUpdate.PacketTool) | +| Bowl | Hub Samples 中集成 | 监控进程异常并导出失败信息 | [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl) | +| Differential | Hub Samples + Core 默认集成 | 对 old/new 文件生成差分,并在更新阶段应用 | [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential) | ## Step 1:准备仓库和运行环境 @@ -239,11 +239,11 @@ Hub 提供了 9 个内置样例,覆盖了 GeneralUpdate 的主要使用场景 跑通这条链路后,建议按顺序阅读: -1. [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md):更新策略、事件通知、静默更新、manifest 极简配置。 -2. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md):补丁包、Hash、OSS Config、Simulation。 -3. [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential.md):差分算法、并行处理和 Clean/Dirty。 -4. [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl.md):崩溃监控、备份和失败恢复。 -5. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool.md):补丁包、Hash、OSS Config、Simulation。 +1. [GeneralUpdate.Core](../doc/GeneralUpdate.Core):更新策略、事件通知、静默更新、manifest 极简配置。 +2. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool):补丁包、Hash、OSS Config、Simulation。 +3. [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential):差分算法、并行处理和 Clean/Dirty。 +4. [GeneralUpdate.Bowl](../doc/GeneralUpdate.Bowl):崩溃监控、备份和失败恢复。 +5. [GeneralUpdate.Tools](./GeneralUpdate.PacketTool):补丁包、Hash、OSS Config、Simulation。 ## Sample UI diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/quickstart/GeneralUpdate.PacketTool.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/quickstart/GeneralUpdate.PacketTool.md index c269482..2bd4907 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/quickstart/GeneralUpdate.PacketTool.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/quickstart/GeneralUpdate.PacketTool.md @@ -144,7 +144,7 @@ dotnet run --project GeneralUpdate.Tools.csproj - Extension Host 调用 `ExtensionManager.QueryRemoteExtensionsAsync(...)` 获取扩展列表 - 安装时下载 ZIP,读取 `manifest.json` 进行兼容性检查和依赖解析 -- 详见 [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension.md) +- 详见 [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension) --- @@ -189,7 +189,7 @@ dotnet run --project GeneralUpdate.Tools.csproj - 将 `oss_config.json` 上传到 OSS bucket 或静态文件服务器 - OSS 客户端读取此文件发现可用版本,下载后校验 Hash -- 详见 [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md) OSS 更新策略 +- 详见 [GeneralUpdate.Core](../doc/GeneralUpdate.Core) OSS 更新策略 --- @@ -349,7 +349,7 @@ Generate Sample 额外输出: ## 关联文档 -- [GeneralUpdate.Core](../doc/GeneralUpdate.Core.md):Client/Upgrade 更新主流程 -- [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential.md):差分算法 Clean/Dirty 模式 -- [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension.md):扩展包安装与版本管理 -- [入门实战手册](./Beginner%20cookbook.md):从零跑通完整更新闭环 +- [GeneralUpdate.Core](../doc/GeneralUpdate.Core):Client/Upgrade 更新主流程 +- [GeneralUpdate.Differential](../doc/GeneralUpdate.Differential):差分算法 Clean/Dirty 模式 +- [GeneralUpdate.Extension](../doc/GeneralUpdate.Extension):扩展包安装与版本管理 +- [入门实战手册](./Beginner%20cookbook):从零跑通完整更新闭环