Как работает Viewer

Жизненный цикл запросов документа

Doconut отображает документы в виде постраничных изображений, обслуживаемых через middleware 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‑view используйте @inject Doconut.Viewer Viewer. Кэши экземпляров Viewer, принадлежащие приложению, больше не нужны — и более не корректны, поскольку они не содержат ничего, что стоило бы кэшировать.

Что происходит внутри OpenDocumentAsync

  1. Лицензионный шлюз. Отклонённая или просроченная версия лицензии (в чёрном списке, подделана, или сборка вне окна обновления лицензии) немедленно бросает LicenseException с причиной отклонения в сообщении — открытие никогда не деградирует тихо при недействительной (в отличие от отсутствующей) лицензии. В случае календарно‑просроченной временной или подписной лицензии происходит исключение: она не бросает исключение — переходит в режим водяного знака.
  2. Создание сеанса. Фабрика просмотрщика выбирает подходящий просмотрщик формата по расширению файла и загружает документ (см. Конвейер рендеринга). Сеанс сохраняется в IMemoryCache под новым GUID‑токеном с скользящим истечением — DocOptions.TimeOut минут, по умолчанию 60. Каждый запрос страницы сбрасывает таймер.
  3. Регистрация безопасности. При UnsafeMode = false (по умолчанию) токен привязывается к ASP.NET‑сеансу вызывающего: в сеанс записывается маркер secure-{token}, поэтому только тот браузерный сеанс, который открыл документ, может запрашивать его страницы.
  4. Токен возвращается. Это единственное удостоверение для всего дальнейшего.

Отдельного шага прогрева нет. В предыдущей библиотеке для этой цели был InitCache(); он больше не существует, и ничто его не заменяет.

Три перегрузки различаются только входными данными: путь к файлу, путь к файлу плюс конфигурация формата (PdfConfig, WordConfig, …) или Stream плюс FileInfo, расширение которого определяет формат.

Как виджет получает страницы

Виджет клиента вызывает middleware Doconut, передавая токен в строке запроса. Действия middleware зависят от запроса:

ЗапросНазначение
?token=…&page=NИзображение отрисованной страницы (PNG)
?token=…&page=N&thumb=1Миниатюра
?token=…&zoom=…Отрисовка страницы с масштабом
?token=…&search=termПолнотекстовый поиск (по лицензии)
?token=…&bookmarksОглавление/закладки документа
?token=…&copy / &showlinks / &fileFormatКопирование текста, гиперссылки и информация о формате
?token=…&metaТехнические метаданные DICOM; возвращает 501 для DICOM‑сеанса в этом пакете
?token=…&action=rotate/flip/closeДействия со страницей и явное закрытие
?token=…&AnnSave=… / &AnnLoadСохранение/загрузка аннотаций

Каждый из этих путей сначала проходит проверку:

  • Отсутствует токен → middleware возвращает 404 (или баннер версии, когда ShowDoconutInfo = true).
  • Неизвестный или просроченный токен → изображение ошибки с текстом Document session not found. Please re-open document.
  • Отсутствует middleware сеанса (при UnsafeMode = false) → HTTP 500 с сообщением Session middleware not configured. Call UseSession() before UseDoconut().
  • Токен открыт другим браузерным сеансом → изображение ошибки с текстом You Are Not Authorized To View This Page.

Где монтируется middleware

UseDoconut() отвечает на запросы, содержащие токен, где бы вы его ни разместили. Справочное приложение сохраняет историческую форму запроса DocImage.axd на ветке MapWhen, поэтому существующий клиент продолжает вызывать тот же URL. Как бы вы ни решили, BasePath клиента должен соответствовать ему — см. ViewerConfig.

Обслуживание страниц из общего хранилища вместо узла открытия — это совершенно другая схема; см. Distributed Deployments.

Закрытие документа

csharp
viewer.CloseDocument(token);

CloseDocument удаляет сеанс из кэша (что освобождает движок документа и сразу освобождает его память), удаляет маркер secure-{token} и отзывает право доступа. Вызывать его необязательно — скользящее истечение делает ту же очистку автоматически — но для больших документов это вежливый способ освободить память сразу после завершения работы пользователя.

Он вызывается у внедрённого Viewer. Предыдущий шаблон — создание краткоживущего Viewer в блоке using исключительно для закрытия токена — теперь не имеет аналога и не нужен.

Основные выводы

  • Один открытый документ = один сеанс = один токен. Токены привязаны к браузерному сеансу, а не к глобальным URL.
  • Токен истекает по скользящему окну; если просмотрщик простоял дольше DocOptions.TimeOut, требуется повторное открытие.
  • Viewer можно внедрять и свободно делить; все состояние хранится в сеансах.

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