快速入门

在 MVC 5 或 Web Forms 中渲染您的第一个文档

本指南将一个基于 .NET Framework 4.7.2 的 ASP.NET 应用程序从已安装的包转化为在浏览器中渲染的文档:Global.asax、一个打开端点、查看器的资源、小部件以及连接它们的令牌。它假设已完成来自安装的 Web.config 注册——如果没有 DocImage.axd 处理程序和资源模块,下面的内容将无法访问服务器。

在 Global.asax 中组合 Doconut

MVC 5 应用程序的完整 Application_Start 如下:

csharp
// Global.asax.cs - compose once per worker process, release on shutdown.
protected void Application_Start()
{
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

    Doconut.DoconutHost.Initialize(options =>
    {
        options.LicensePath = HostingEnvironment.MapPath("~/wwwroot/Doconut.Viewer.lic");
        options.UnsafeMode  = false; // a page is served only to the session that opened the document
    });

    // MVC only: keep the two Doconut endpoints out of routing.
    RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    RouteTable.Routes.IgnoreRoute("doconut-res/{*pathInfo}");
}

protected void Application_End()
{
    Doconut.DoconutHost.Shutdown();
}
  • Encoding.RegisterProvider 使某些文档格式使用的旧版代码页对进程可用。请在首次打开文档之前调用一次。
  • UnsafeMode = false 是默认设置,显式写出以防止有人意外更改:DocImage.axd 只向打开文档的 ASP.NET 会话提供页面。
  • 两个 IgnoreRoute 调用 将 MVC 路由排除在 DocImage.axd 和 /doconut-res/* 之外,这些由处理程序和模块提供服务。Web Forms 应用程序没有 MVC 路由,因此不需要这两行。
  • DoconutHost.Shutdown 在应用程序池回收时停止 Doconut 的后台工作并释放其服务。即使从未运行 Initialize,调用它也是安全的。

打开文档

打开文档会返回一个不透明的令牌。小部件在每个页面请求中都会发送该令牌。

在 MVC 5 中,控制器操作从 DoconutHost.Services 中解析 Viewer 服务——MVC 5 不支持构造函数注入:

csharp
// POST /Document/Open
[HttpPost]
public async Task<ActionResult> Open()
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();

    // The token is opaque - hand it to the viewer, never log or persist it.
    string token = await viewer.OpenDocumentAsync(Server.MapPath("~/files/Sample.pdf"));
    return Json(new { token });
}

文档必须在具有会话状态的请求内部打开,因为打开文档会授予当前 ASP.NET 会话访问其页面的权限。MVC 控制器和 Web Forms 页面默认拥有会话状态。自定义 IHttpHandler 没有会话状态,除非实现 System.Web.SessionState.IRequiresSessionState。如果在没有会话状态的处理程序中打开文档,查看器将在每个页面上显示 “您未被授权查看此页面。”。

在 Web Forms 中,页面本身可以在其代码隐藏文件中打开文档。OpenDocument 是 OpenDocumentAsync 的同步形式,适用于不使用异步页面的代码隐藏文件:

csharp
// Default.aspx.cs
protected string Token;

protected void Page_Load(object sender, EventArgs e)
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
    Token = viewer.OpenDocument(Server.MapPath("~/files/Sample.pdf"));
}

将令牌视为持有者凭证:切勿记录或持久化它;仅交给小部件使用。当文档会话过期时,它将失效——请重新打开文档以获取新的令牌。

引用查看器资源

Viewer.ReferenceCss 和 Viewer.ReferenceScripts 会按依赖顺序发出指向 /doconut-res/ 的 <link> 和 <script> 标签,用于嵌入的资源。该小部件是 jQuery 插件,所以 jQuery 必须先加载。

MVC 5。 视图没有 @inject,因此在控制器中生成标签并通过 ViewBag 传递:

csharp
public ActionResult Index()
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
    ViewBag.ViewerCss = viewer.ReferenceCss(new CssConfig
    {
        IncludeBootstrapCss = true,
        IncludeViewerCss    = true
    });
    ViewBag.ViewerScripts = viewer.ReferenceScripts(new ScriptConfig
    {
        IncludeJQuery        = true,
        IncludeBootstrap     = true,
        IncludeViewerScripts = true
    });
    return View();
}
html
@Html.Raw(ViewBag.ViewerCss)
...
@Html.Raw(ViewBag.ViewerScripts)

把这段工作放在控制器中。MVC 5 在首次请求时编译视图,如果没有额外的编译器包,编译器只能理解 C# 5 —— 不支持 ?.、不支持 $"..."、不支持 nameof。在 Visual Studio 中使用这些特性的视图可以正常编译,但在浏览器中会返回空的 500 错误。

Web Forms。 在代码隐藏文件中暴露相同的字符串,并使用 <%= %> 输出:

html
<head>
    <%= ViewerCss %>
</head>
<body>
    ...
    <%= ViewerScripts %>
</body>

IncludeViewerCss 和 IncludeViewerScripts 是必需的核心。若要添加搜索和批注功能区,还需设置 IncludeSearchCss、IncludeAnnotationCss、IncludeSearchScripts、IncludeSearchBar、IncludeAnnotationScripts 和 IncludeAnnotationBar。当许可证未授予相应功能时,这两种方法都会省略对应功能区的文件;查看器本身仍然会启动。

将查看器添加到页面

渲染区域是两个嵌套的 div:

html
<div id="divDocViewer">
    <div id="div_ctlDoc"></div>
</div>

为外层 div 设置真实的高度 —— 查看器会填满其容器并在启动时测量它。查看器本身不绘制工具栏:GotoPage、Next、Zoom、FitType 等方法需要页面自行绑定到自己的按钮,这些按钮放在查看器旁边:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <button type="button" onclick="objViewer.GotoPage(1)">First</button>
    <button type="button" onclick="objViewer.Next(false)">Previous</button>
    <button type="button" onclick="objViewer.Next(true)">Next</button>
    <button type="button" onclick="objViewer.Zoom(false)">Zoom out</button>
    <button type="button" onclick="objViewer.Zoom(true)">Zoom in</button>
    <button type="button" onclick="objViewer.FitType('width')">Fit width</button>
</nav>

初始化查看器并显示文档

在加载查看器脚本后,创建小部件并传入令牌:

javascript
var objViewer = $('#div_ctlDoc').docViewer({
    showThumbs: true,
    autoLoad:   false,
    pageZoom:   100,
    FitType:    'width',
    BasePath:   '/',                   // '/' 表示站点根目录下的 DocImage.axd
    ResPath:    'doconut-res/images',  // 不带前导 '/'
    onViewerReady: function () {
        // 页面已可见;此处隐藏加载指示器
    },
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

fetch('/Document/Open', { method: 'POST' })
    .then(function (resp) { return resp.json(); })
    .then(function (data) { objViewer.View(data.token); });

在 Web Forms 中,如果页面本身打开文档,则最后三行改为 objViewer.View('<%= Token %>');。

以下四个细节若处理不当会导致失败且没有错误提示:

  • BasePath: '/' 指向站点根目录下的 DocImage.axd。如果应用部署在虚拟目录中,需要使用不带斜杠的目录名(例如 'myapp',请求 /myapp/DocImage.axd)。带前导斜杠('/myapp')会生成 //myapp/DocImage.axd,浏览器会将其解释为对另一个主机的请求。
  • ResPath: 'doconut-res/images' —— 图标位于资源根目录的下一级文件夹。使用 'doconut-res' 时,每个图标都会返回 404:工具栏图标和加载指示器消失,但文档本身仍会渲染。前导斜杠会导致同 BasePath 一样的错误。
  • 回调是选项。 onViewerReady、onError 等必须写在传给 docViewer(...) 的对象里。像 ctlDoc_ViewerReady 这样的全局函数不会被调用。
  • 先保留查看器实例,再单独调用 View。 docViewer(...) 返回包含 Zoom、Next、FitType 等方法的对象;View(...) 返回其他东西。写成 var objViewer = $('#div_ctlDoc').docViewer({...}).View(token) 会渲染文档,但随后第一次点击工具栏时会出现 objViewer.Zoom is not a function 错误。

选项名称混用了 camelCase(showThumbs、autoLoad、pageZoom)和 PascalCase(FitType、BasePath、ResPath)。拼写错误的选项会被忽略,组件会使用默认值。

单行替代方案

对于只需要显示文档且不需要自定义工具栏的页面,Viewer.RenderViewer 会生成完整的初始化脚本——docViewer(...),其中 BasePath: '/' 和 ResPath: 'doconut-res/images',随后调用 .View(token)。该功能仅在此包中提供:

csharp
// Web Forms code-behind, after opening the document
ViewerInit = viewer.RenderViewer("div_ctlDoc", Token);
html
<div id="div_ctlDoc" style="height: 100vh"></div>
<%= ViewerScripts %>
<%= ViewerInit %>

将 ViewerConfig 作为第三个参数传入,可更改缩放、适配方式或缩略图面板。由于生成的脚本不保留对查看器的引用,当页面需要自己的工具栏时,请使用上述手写的初始化代码。

关闭文档

当用户离开查看器或打开替代文档时,调用 objViewer.Close(true);它会清除部件并向 DocImage.axd 发送关闭请求。未传参的 objViewer.Close() 只会清除部件本身。在服务器端,viewer.CloseDocument(token) 会立即移除文档会话,释放渲染资源,并撤销会话对页面的访问。滑动过期最终也会产生相同效果,但显式关闭大型文档可以更快释放内存。

运行它

将 PDF 放置在 ~/files/Sample.pdf,在 IIS Express 中启动站点(在 Visual Studio 中按 F5),然后打开承载部件的页面。首个页面会渲染,并在左侧显示缩略图。如果未渲染,请参见故障排除。

未授权时的获取内容

缺少许可证不会抛出异常。查看器仍会正常渲染,但每页都会带有评估水印。有关 Doconut 查找许可证的位置,请参见许可证设置。

此页面对您有帮助吗?