许可证设置

Doconut 查找许可证文件的位置

如果没有许可证,Doconut 仍然可以渲染文档——每页都会带有评估水印。本页介绍提供许可证的四种方式,以及当设置多个时的确切优先级。

许可证在 一次,在 AddDoconut() 期间加载。这与之前的 .NET Standard 库不同,之前是将许可证传递给 Viewer 实例或通过静态 Viewer.DoconutLicense(...) / Viewer.SetLicensePlugin(...) 调用设置。这些方法在此版本中不再作为启动机制——请参阅 迁移指南。

提供许可证的四种方式

共有四种方式:DoconutOptions 上的三种显式来源——流、原始内容或文件路径——以及当它们都未设置时的自动发现。当设置多个时,优先级如下:

LicenseStream 胜过 LicenseContent 胜过 LicensePath 胜过 自动搜索。

按路径

LicensePath 会原样传递给 File.Exists。相对路径会相对于进程的 当前工作目录 解析——而不是你的项目文件夹,也不是 Program.cs 所在的文件夹。如果路径无法解析,Doconut 不会抛出异常,也 不会 回退到自动搜索——它仅仅是没有加载许可证,查看器会显示水印。只有在 LicensePath、LicenseContent 或 LicenseStream 均未设置时,才会运行自动搜索。相同文件夹中的每插件许可证文件 (Doconut.Viewer.<Capability>.lic) 会一起读取,因此当插件许可证位于基础许可证旁边时,LicensePath 与 AddPlugin<...>() 可以协同工作。

建议使用绝对路径(例如由 IWebHostEnvironment.WebRootPath 或 AppContext.BaseDirectory 构建),或者完全省略 LicensePath,依赖下面的自动发现。

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "Doconut.Viewer.lic");
});

按流

LicenseStream 在启动时读取一次——当许可证来自密钥存储而不是磁盘文件时非常有用。

csharp
// 优先级:LicenseStream > LicenseContent > LicensePath > 自动搜索.
builder.Services.AddDoconut(options =>
{
    options.LicenseStream = licenseStream;
});

按内容

LicenseContent 接受许可证文本本身——可以来自环境变量、数据库或密钥管理器:

csharp
// 来自数据库、环境变量或密钥管理器的许可证 XML —
// 没有磁盘文件。仅被 LicenseStream 超过。
builder.Services.AddDoconut(options =>
{
    options.LicenseContent = Environment.GetEnvironmentVariable("DOCONUT_LICENSE") ?? "";
});

自动发现

如果未配置这三种显式来源,Doconut 将自行搜索许可证:

csharp
// 未配置任何内容,Doconut 将在以下两个文件夹中自行搜索许可证,仅限这两个文件夹:
//   1. {CurrentDirectory}/wwwroot
//   2. {AppContext.BaseDirectory}/wwwroot   (构建输出文件夹内的 wwwroot)
// 它首先查找 Doconut.lic,然后是 Doconut.Viewer.lic,以及任何每个功能的
// Doconut.Viewer.<Capability>.lic 文件,并列于其旁边。
builder.Services.AddDoconut();

探测文件夹的顺序以及每个文件夹中查找的文件名如下:

text
1. {CurrentDirectory}/wwwroot
2. {AppContext.BaseDirectory}/wwwroot

文件名(按上述每个文件夹的检查顺序):
  Doconut.lic                          — 基础查看器许可证(首先检查)
  Doconut.Viewer.lic                   — 基础查看器许可证
  Doconut.Viewer.<Capability>.lic      — 每个功能的许可证,位于基础许可证旁边

不会读取 wwwroot 文件夹之外的任何内容:输出文件夹本身或 wwwroot/lib 中的 .lic 文件都会被忽略。

如果你之前向 Viewer 构造函数传递了 List<string> 类型的许可证路径(每个功能一个),请将这些伴随文件放在 Doconut.Viewer.lic 旁边,让自动发现机制拾取它们。DoconutOptions 没有路径列表选项。

将许可证复制到输出文件夹

当进程不是从项目文件夹运行时——例如已发布的应用、IIS、容器或测试运行器——第一次探测会失败,第二次探测 {AppContext.BaseDirectory}/wwwroot 必须找到该文件。SDK 自带的测试应用在每次构建时都会使用以下 MSBuild 目标将许可证复制到该位置:

xml
<Target Name="CopyLicensesToOutput" AfterTargets="Build">
  <ItemGroup>
    <DoconutLicenseFiles Include="$(MSBuildProjectDirectory)\wwwroot\*.lic" />
    <StaleOutputLicenses Include="$(OutDir)wwwroot\*.lic" />
  </ItemGroup>
  <!-- 先删除,以便在 wwwroot 中重命名或删除 .lic 时也能将其从输出中移除。 -->
  <Delete Files="@(StaleOutputLicenses)" />
  <Copy SourceFiles="@(DoconutLicenseFiles)" DestinationFolder="$(OutDir)wwwroot" />
</Target>

目标必须是 $(OutDir)wwwroot:复制到输出根目录的 .lic 文件永远不会被自动搜索到。本页上的 LicensePath 示例指向同一位置。

将 .lic 文件排除在源代码控制之外——将它们与应用一起部署,或通过 LicenseContent 或 LicenseStream 从机密存储注入许可证。

没有许可证会怎样

缺少许可证不会抛出异常。AddDoconut() 成功执行,应用启动,查看器运行——但每页都会带有评估水印,且不会授予任何可选功能。

如果找到了许可证文件但被拒绝,则情况不同。签名无效、被篡改、列入黑名单,或构建时间超出许可证的版本窗口,都会导致 OpenDocumentAsync 抛出 LicenseException,并附带 License.RejectionMessage。日历已过期且没有拒绝信息的许可证会继续以水印模式运行。

插件需要相应的功能

为未对应授权的插件注册会产生不同的结果:对于缺少许可证、遗留的 TRIAL 文件,或未包含该功能的付费许可证,AddDoconut() 会抛出 InvalidOperationException,导致应用无法启动。例如,注册 Converter 插件但没有授予 Converter 的许可证:

text
InvalidOperationException: The Converter plugin is registered via AddPlugin but no active
license grants Converter. Remove the AddPlugin<...>() call or install a license (or
trial/demo) that includes Converter.

该信息直接告诉你如何修复:要么移除该插件的 options.AddPlugin<...>() 调用,要么安装包含该功能的付费许可证或有效的临时/演示(NFR)许可证。临时注册允许在到期后仍然存活,以便已配置的应用在运行时降级而不是在重启时崩溃;一旦过期,它们的功能仍会被撤销。

验证已加载的许可证

使用 IDoconutLicenseService(SDK 使用的同一真实来源),可以公开一个经过身份验证的诊断端点或驱动功能标记。请勿返回许可证内容或密钥。

csharp
app.MapGet("/api/doconut/license", (IDoconutLicenseService license) => Results.Ok(new
{
    viewer = license.IsViewerLicensed || license.IsTemporary,
    temporary = license.IsTemporary,
    search = license.IsCapabilityGranted(LicenseCapability.Search),
    annotation = license.IsCapabilityGranted(LicenseCapability.Annotation),
    converter = license.HasConverter,
    dicom = license.HasDicom
}));

许可证在 AddDoconut() 注册期间读取。ResetLicense 目前是一个兼容性属性,没有主动的重新加载路径,因此替换许可证文件需要重新启动应用程序。

故障排查矩阵

症状可能原因检查
查看器工作但每页都有水印未加载许可证,或许可证已过期解析 IDoconutLicenseService;验证输出目录和进程工作目录
AddDoconut() 对插件抛出异常许可证未授予该插件的功能检查 IsCapabilityGranted(...) 并移除未购买的注册
配置的相对路径在本地有效但在 IIS/容器中无效进程工作目录已更改使用 AppContext.BaseDirectory 或绝对路径
替换的 .lic 文件没有效果单例许可证服务已创建重启应用程序
OpenDocumentAsync 抛出 LicenseException签名、域、版本窗口、黑名单或插件运行时门禁拒绝了许可证在不向不受信任的客户端暴露的情况下读取异常/拒绝信息
按功能的许可证以前有效但现在无效旧代码向 Viewer 构造函数传递了显式的许可证路径将伴随的 .lic 文件移动到 Doconut.Viewer.lic 同目录,并让自动发现加载它们

下一步

  • 许可 — 功能、许可层级以及验证运行时加载的内容。
  • 故障排除 — 水印、被拒绝的许可和功能错误。

此页面对您有帮助吗?