Быстрый старт

Отобразите ваш первый документ в MVC 5 или Web Forms

Этот пошаговый гид переводит ASP.NET‑приложение на .NET Framework 4.7.2 из установленного пакета в документ, отображаемый в браузере: Global.asax, открывающая конечная точка, ресурсы просмотрщика, виджет и токен, связывающий их. Предполагается, что регистрации в Web.config, описанные в Установка, уже выполнены — без обработчика DocImage.axd и модуля ресурсов нижеописанное не сможет обратиться к серверу.

Сборка Doconut в Global.asax

Полный Application_Start для приложения MVC 5:

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 действие контроллера получает сервис Viewer из DoconutHost.Services — в 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 генерируют теги <link> и <script> для встроенных ресурсов, в порядке зависимостей, указывая на /doconut-res/. Виджет — это плагин 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. Откройте те же строки из кода‑behind и выведите их с помощью <%= %>:

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:   '/',                   // '/' means DocImage.axd at the site root
    ResPath:    'doconut-res/images',  // no leading '/'
    onViewerReady: function () {
        // pages are visible; hide a loading indicator here
    },
    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 (F5 в Visual Studio) и откройте страницу, содержащую виджет. Первая страница отрисовывается, с миниатюрами слева. Если этого не происходит, см. Устранение проблем.

Что вы получаете без лицензии

Отсутствие лицензии не вызывает ошибку. Просмотрщик отрисовывается нормально, но каждая страница содержит водяной знак оценки. См. Настройка лицензии, где Doconut ищет вашу лицензию.

Была ли эта страница полезной?