查看器工作原理

文档请求生命周期

Doconut 将文档渲染为分页图像,并通过 ASP.NET Core 中间件提供服务。理解整个生命周期——打开、令牌、页面请求、关闭——几乎可以解释您观察到的所有行为,包括错误信息。

三个关键部分

  • Viewer — 您注入的公共服务。它打开文档并返回会话令牌。
  • 文档会话 — 服务器端对象,持有已加载的文档,使用 IMemoryCache 中的令牌作为键。
  • Doconut 中间件 — 通过 UseDoconut() 添加;响应浏览器小部件发出的每个请求(pages、thumbnails、search、annotations …),始终通过令牌进行身份验证。

Viewer 是无状态的 —— 设计如此

Viewer 为 sealed 类型,不保存每次请求的文档状态,且刻意 不 实现 IDisposable。会话独立存在于会话管理器中,并由缓存过期或显式调用 CloseDocument(token) 清理。

您不再需要自行实例化它。AddDoconut() 将其注册为瞬态服务;在需要的地方注入即可:

csharp
app.MapPost("/api/open", async (string fileName, Viewer viewer) =>
{
    var token = await viewer.OpenDocumentAsync($"files/{fileName}");
    return Results.Content(token, "text/plain");
});

在 MVC 应用中,可将其作为构造函数参数;在 Razor 视图中,使用 @inject Doconut.Viewer Viewer。不再需要自行维护 Viewer 实例的缓存——也不再正确,因为它们并不持有任何值得缓存的内容。

OpenDocumentAsync 内部发生了什么

  1. 许可证检查。 若许可证被拒绝或已过期(列入黑名单、被篡改,或在许可证更新窗口之外的构建),会立即抛出 LicenseException,并将拒绝原因作为消息——对于无效(而非缺失)许可证,打开操作绝不会悄然降级。日历过期的临时或订阅许可证是例外:它不会抛异常,而是降级为水印。
  2. 会话创建。 查看器工厂根据文件扩展名挑选合适的格式查看器并加载文档(参见渲染管线)。会话以全新 GUID 令牌存入 IMemoryCache,并使用 滑动过期——DocOptions.TimeOut 分钟,默认 60。每一次页面请求都会重置计时。
  3. 安全注册。 当 UnsafeMode = false(默认)时,令牌会绑定到调用者的 ASP.NET 会话:在会话中写入 secure-{token} 标记,只有打开文档的浏览器会话才能请求其页面。
  4. 返回令牌。 该令牌是后续所有操作的唯一凭证。

没有单独的预热步骤。之前的库提供 InitCache() 用于此目的;该方法已不存在,也没有替代实现。

这三个重载唯一的区别在于输入:文件路径、带有每种格式配置的文件路径(PdfConfig、WordConfig …),或带有 FileInfo 的 Stream(其扩展名决定格式检测)。

小部件如何获取页面

客户端小部件在查询字符串中携带令牌调用 Doconut 中间件。中间件的行为取决于具体请求:

查询用途
?token=…&page=N渲染的页面图像(PNG)
?token=…&page=N&thumb=1缩略图
?token=…&zoom=…放大页面渲染
?token=…&search=term全文搜索(受许可证限制)
?token=…&bookmarks文档大纲/书签
?token=…&copy / &showlinks / &fileFormat文本复制、超链接及格式信息
?token=…&metaDICOM 技术元数据;对 DICOM 会话返回 501
?token=…&action=rotate/flip/close页面操作及显式关闭
?token=…&AnnSave=… / &AnnLoad保存/加载批注

这些路径在处理前都会进行以下验证:

  • 没有令牌 → 中间件返回 404(或在 ShowDoconutInfo = true 时返回版本横幅)。
  • 未知或已过期的令牌 → 返回错误图片,显示 Document session not found. Please re-open document.
  • 缺少会话中间件(UnsafeMode = false) → 返回 HTTP 500,提示 Session middleware not configured. Call UseSession() before UseDoconut().
  • 令牌由不同的浏览器会话打开 → 返回错误图片,显示 You Are Not Authorized To View This Page.

中间件的挂载位置

UseDoconut() 会响应携带令牌的请求,无论您将其挂载在哪里。参考应用在 MapWhen 分支上保留了历史的 DocImage.axd 请求形态,以便已有客户端继续使用旧 URL。无论您选择何种路径,客户端的 BasePath 必须与之保持一致 —— 参见 ViewerConfig。

将页面从共享存储而非打开节点提供是一种完全不同的部署方式;请参阅 Distributed Deployments。

关闭文档

csharp
viewer.CloseDocument(token);

CloseDocument 会从缓存中移除会话(这会立即释放底层文档引擎并释放其内存),删除 secure-{token} 标记,并撤销访问授权。调用它是可选的——滑动过期会自动完成相同的清理——但对于大型文档来说,在用户完成后立即释放内存是更礼貌的做法。

它在注入的 Viewer 上调用。之前的模式——在 using 块中构造短生命周期的 Viewer 仅为关闭令牌——已没有对应实现,也不再需要。

要点总结

  • 一个打开的文档对应一个会话对应一个令牌。令牌与浏览器会话绑定,而非全局 URL。
  • 令牌在滑动窗口内过期;如果查看器在 DocOptions.TimeOut 之后仍保持空闲,需要重新打开文档。
  • Viewer 可以自由注入并共享;所有状态都由会话承担。

此页面对您有帮助吗?