Szybki start
Renderuj swój pierwszy dokument w kilka minut
Ten przewodnik przenosi aplikację ASP.NET Core z pustego Program.cs do dokumentu renderowanego w przeglądarce: rejestracja serwera, pełny pakiet Viewer (pasek narzędzi Viewer, montaż Viewer i opcjonalne wstążki wyszukiwania/adnotacji), odwołania do zasobów, inicjalizacja klienta, otwarcie dokumentu i wykonanie.
Aplikacja hostująca musi docelowo korzystać z środowiska uruchomieniowego obsługującego netstandard2.1 — .NET Core 3.0 lub nowszego. Aplikacje referencyjne dostarczane z tym wydaniem celują w net8.0.
Konfiguracja serwera
AddDoconut() rejestruje usługi; UseDoconutResources() i UseDoconut() podłączają middleware. Wywołanie zasobów musi być pierwsze. Wywołania sesji są również wymagane — domyślne zabezpieczenie dokumentu Doconut weryfikuje każde żądanie strony względem stanu sesji ASP.NET. Stan sesji wymaga pamięci podręcznej: AddDistributedMemoryCache() zapewnia jedną. Aplikacja MVC lub Razor Pages rejestruje ją już domyślnie, ale aplikacja minimal-API nie robi tego, i bez niej aplikacja zatrzymuje się przy uruchamianiu z Unable to resolve service for type 'IDistributedCache'. Czy już zarejestrowano Doconut podczas Instalacja? Przejdź do następnej sekcji.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // stan sesji wymaga pamięci podręcznej (MVC/Razor Pages już dodają jedną)
builder.Services.AddSession(); // Bezpieczeństwo dokumentów Doconut opiera się na stanie sesji ASP.NET
app.UseSession(); // wywołaj UseSession() przed UseDoconut()
app.UseDoconutResources(); // musi być zarejestrowany przed UseDoconut()
app.UseDoconut();Aplikacja referencyjna zachowuje historyczny kształt żądania DocImage.axd i montuje middleware dokumentu na pasującej gałęzi. To jest układ do skopiowania, gdy przenosisz istniejącą aplikację .NET Standard, ponieważ klient nadal wywołuje ten sam URL, co zawsze:
builder.Services.AddDoconut(options =>
{
options.UnsafeMode = false; // zachowaj włączone sprawdzanie sesji Doconut's
options.ShowDoconutInfo = false;
});
builder.Services.AddDistributedMemoryCache(); // stan sesji wymaga pamięci podręcznej (MVC/Razor Pages już dodają jedną)
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(3600);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// Wymagane dla kodowań wielobajtowych w niektórych formatach dokumentów.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseDoconutResources(); // PRZED gałęzią dokumentu
app.MapWhen(
context => context.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
branch => branch.UseDoconut());UseDoconut() nie przyjmuje żadnych opcji w tym wydaniu — wszystko przeniesiono do AddDoconut().
MiddlewarePath jest wartością koordynacyjną; nie mapuje gałęzi ASP.NET Core samodzielnie. ResourcesPath udostępnia osadzony pakiet pod /doconut-res, a ścieżka zasobu obrazu widgetu jest więc ResPath: 'doconut-res/images'.
Dodaj przeglądarkę do strony
Viewer jest wymaganą podstawą strony. Jego powierzchnia renderowania używa dwóch zagnieżdżonych div:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Traktuj pasek narzędzi, montaż modułów i powierzchnię Viewer jako jedną kompozycję strony. Wyszukiwanie i Adnotacja wstrzykują swoje osadzone wstążki do opcjonalnych montażów, ale te moduły nigdy nie są samodzielne: zawsze dołączają do Viewer na tej samej stronie.
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Nawigacja, powiększenie, Search i Annotation przyciski -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Odwołaj się do zasobów przeglądarki
W widoku Razor wstrzyknięta usługa Viewer generuje znaczniki <link> i <script> przeglądarki w kolejności zależności — widget jest wtyczką jQuery, więc jQuery musi być załadowane przed skryptami przeglądarki:
@inject Doconut.Viewer Viewer
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
IncludeBootstrapCss = true,
IncludeViewerCss = true
}))
@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
IncludeJQuery = true,
IncludeBootstrap = true,
IncludeViewerScripts = true
}))Aby uzyskać kompletny pakiet Viewer, żądaj zasobów Viewer i modułów razem:
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
IncludeBootstrapCss = true,
IncludeViewerCss = true,
IncludeSearchCss = true,
IncludeAnnotationCss = true
}))
@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
IncludeJQuery = true,
IncludeBootstrap = true,
IncludeViewerScripts = true,
IncludeSearchScripts = true,
IncludeSearchBar = true,
IncludeAnnotationScripts = true,
IncludeAnnotationBar = true
}))IncludeViewerCss i IncludeViewerScripts są obowiązkowymi flagami rdzenia. Nigdy nie publikuj przykładu wstążki Search lub Annotation bez nich, montażu Viewer i instancji docViewer. ReferenceCss i ReferenceScripts pomijają zasoby opcjonalnego modułu, gdy bieżąca licencja nie przyznaje tej funkcji; rdzeniowy Viewer nadal się uruchamia.
Te wywołania zastępują ręcznie kopiowane pliki skryptów. Jeśli aplikacja nadal ładuje własne docViewer.js, documentLinks.js lub docViewer.UI.js, usuń te znaczniki, gdy strona działa z wygenerowanych zasobów — zobacz przewodnik migracji.
Inicjalizacja przeglądarki
Widget po stronie klienta jest wtyczką jQuery. Oto minimalny zestaw rzeczywistych opcji inicjalizacji (nie pseudokod):
let searchBar = null;
let annBar = null;
const objViewer = $('#div_ctlDoc').docViewer({
showThumbs: true,
autoLoad: false,
pageZoom: 100,
FitType: 'width',
BasePath: '/',
ResPath: 'doconut-res/images',
onViewerReady: function () {
// strony są widoczne; można bezpiecznie ukryć wskaźnik ładowania tutaj
},
// Przekaż zdarzenia cyklu życia adnotacji do osadzonej wstążki, gdy jest obecna.
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: function (message) {
console.error('Doconut viewer error:', message);
}
});Dwa szczegóły decydują, czy ta strona w ogóle działa:
- Callbacki są opcjami.
onViewerReady,onViewerBusy,onCopyionAutoLoadStatussą przekazywane w obiekcie opcji. Poprzedni viewer wyprowadzał nazwy globalnych funkcji z identyfikatora kontenera i je wywoływał; ten nie robi tego. Obsługa zadeklarowana wyłącznie jako globalna funkcja nigdy nie zostaje uruchomiona i nic nie jest zgłaszane. ResPathwskazuje na prefiks zasobów, a nie na korzeń aplikacji. Przy domyślnymResourcesPath, wartość ta wynosidoconut-res/images. Jeśli zmieniłeśResourcesPath, użyj własnego prefiksu.
Oba te przypadki kończą się cichym niepowodzeniem — zobacz Rozwiązywanie problemów oraz przewodnik migracji.
Wielkość liter w opcjach jest naprawdę mieszana — showThumbs, autoLoad i pageZoom są w stylu camelCase, ale FitType, BasePath i ResPath w stylu PascalCase. Nie ma spójnej reguły; błędne użycie wielkości liter powoduje, że opcja jest cicho ignorowana (widget przełącza się na domyślne ustawienia zamiast zgłaszać błąd).
Zbuduj kompletny pakiet Viewer
Obie aplikacje referencyjne instalują następujące części razem na jednej stronie:
| Część pakietu | Wymaganie | Jak jest połączona |
|---|---|---|
Zasoby Viewer, montaż i objViewer | Wymagane | Rdzeniowy renderator dokumentu |
| Pasek narzędzi Viewer | Wymagany w kompozycji referencyjnej | Znacznik hosta; przyciski wywołują ten sam objViewer |
| Wstążka wyszukiwania | Opcjonalny, licencjonowany moduł | doconutSearchBar(...).attach(objViewer) |
| Wstążka adnotacji | Opcjonalny, licencjonowany moduł | doconutAnnotationBar(...).attach(objViewer) |
Chociaż główny pasek narzędzi Viewer jest znacznikiem hosta, jest instalowany razem z Viewer i nigdy nie powinien być dokumentowany jako odrębny kontroler. Dzięki temu jego układ, etykiety, ikony i reguły autoryzacji pozostają pod kontrolą Twojej aplikacji, a każdy przycisk steruje tą samą instancją Viewer:
<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.GotoPage(objViewer.TotalPages())">Last</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>
<button type="button" onclick="objViewer.FitType('height')">Fit height</button>
<button type="button" id="openSearch">Search</button>
<button type="button" id="openAnnotations">Annotations</button>
</nav>Zachowaj kolejność inicjalizacji pakietu używaną przez obie aplikacje referencyjne:
- Wygeneruj CSS dla Viewer i licencjonowanych modułów.
- Wyrenderuj pasek narzędzi Viewer, montaż wyszukiwania/adnotacji oraz montaż Viewer razem.
- Wygeneruj skrypty dla Viewer i licencjonowanych modułów.
- Załaduj własny skrypt pomocniczy paska narzędzi aplikacji hosta.
- Zainicjalizuj
docVieweri zachowaj wynikowyobjViewer. - Zainicjalizuj każdą licencjonowaną wstążkę Search lub Annotation.
- Wywołaj
attach(objViewer)na każdej wstążce. - Otwórz dokument i zachowaj jego token do żądań Viewer i modułów.
Zabezpieczenia po stronie serwera są ważne: gdy opcjonalna funkcja jest niedostępna, jej skrypt nie jest generowany, więc funkcja wtyczki jQuery nie istnieje.
<script>
let currentToken = '';
const refitViewer = () =>
requestAnimationFrame(() => objViewer.Refit());
@if (Viewer.IsSearchEnabled)
{
<text>
searchBar = $('#searchBarMount').doconutSearchBar({
docId: 'ctlDoc',
getRequestParams: () => ({ token: currentToken }),
onLayout: refitViewer
});
searchBar.attach(objViewer);
</text>
}
@if (Viewer.IsAnnotationEnabled)
{
<text>
annBar = $('#annBarMount').doconutAnnotationBar({
docId: 'ctlDoc',
getRequestParams: () => ({ token: currentToken }),
onLayout: refitViewer
});
annBar.attach(objViewer);
</text>
}
document.getElementById('openSearch').addEventListener('click', () => {
if (!searchBar) return;
searchBar.isOpen() ? searchBar.close() : searchBar.open();
});
document.getElementById('openAnnotations').addEventListener('click', () => {
if (!annBar) return;
annBar.isOpen() ? annBar.close() : annBar.open();
});
</script>Oba osadzone komponenty generują własny DOM wstążki. Search zawiera grupy Find, Options i Results. Annotation zawiera narzędzia do tworzenia, kontrolki stylu, akcje zapisu oraz opcjonalne akcje eksportu/obrazu. Paski udostępniają open(), close(), reset() i isOpen(); zawsze wywołaj attach(objViewer) raz po ich utworzeniu.
Powyższy przykład pomija opcjonalne wywołania zwrotne hosta oraz endpointy eksportu/obrazu dla Annotation, aby zminimalizować uruchamianie. Zobacz Wyszukiwanie i Adnotacje dla pełnej konfiguracji specyficznej dla funkcji, lub Niestandardowe motywy, aby stylizować lub zastąpić pasek narzędzi Viewer zarządzany przez hosta.
Otwórz dokument
Po stronie serwera jest jeden punkt końcowy: wstrzyknięta usługa Viewer otwiera dokument i zwraca token sesji. Otwieranie jest asynchroniczne w tej wersji.
app.MapPost("/api/open", async (Viewer viewer) =>
{
// Token jest nieprzezroczysty — przekaż go do widgetu, nigdy nie loguj ani nie zapisuj go.
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { token });
});Klient pobiera ten token i przekazuje go do widżetu przy użyciu objViewer.View(token):
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});Nie ma nic do wstępnego przygotowania przed pierwszym otwarciem. Jeśli przenosisz aplikację dalej, usuń wszystkie wywołania InitCache() oraz flagę je chroniącą — metoda już nie istnieje.
Zamknij dokument
Wywołaj objViewer.Close(), gdy użytkownik opuszcza przeglądarkę lub otwiera zastępczy dokument. W przepływach sterowanych przez serwer, viewer.CloseDocument(token) natychmiast usuwa pamięć podręczną sesji, zwalnia silnik renderujący, usuwa jego znacznik bezpieczeństwa i unieważnia token. Przesuwające się wygaśnięcie ostatecznie wykonuje tę samą czynność, ale zaleca się jawne zamknięcie przy dużych dokumentach.
Pełny przepływ żądania wygląda następująco:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentTraktuj token jak poświadczenie typu bearer: nigdy go nie loguj, nigdy nie przechowuj, przekazuj go wyłącznie widżetowi. Identyfikuje on aktywną sesję dokumentu na serwerze i przestaje działać, gdy sesja wygaśnie — ponownie otwórz dokument, aby uzyskać nowy token.
Uruchom to
Umieść plik PDF w wwwroot/files/Sample.pdf, uruchom dotnet run i otwórz stronę, na której znajduje się widżet. Pierwsza strona jest renderowana w przeglądarce, z panelem miniatur po lewej. Jeśli tak się nie dzieje, zobacz Rozwiązywanie problemów.
Co otrzymujesz bez licencji
Brak licencji nie powoduje błędu. Przeglądarka renderuje normalnie, ale każda strona zawiera znak wodny oceny. Zobacz Ustawienia licencji, aby dowiedzieć się, jak Doconut znajduje licencję i co się zmienia, gdy ją znajdzie.
Czy ta strona była pomocna?