Schnellstart

Rendern Sie Ihr erstes Dokument in Minuten

Dieses Tutorial führt eine ASP.NET Core‑App von einem leeren Program.cs zu einem im Browser gerenderten Dokument: Serverregistrierung, das komplette Viewer‑Paket (Viewer‑Symbolleiste, Viewer‑Mount und optionale Such‑/Annotations‑Bänder), Asset‑Referenzen, Client‑Initialisierung, Dokumentöffnung und Ausführung.

Servereinrichtung

AddDoconut() registriert die Dienste; UseDoconutResources() und UseDoconut() verbinden die Middleware. Der Aufruf der Ressourcen muss zuerst erfolgen. Die Sitzungsaufrufe sind ebenfalls erforderlich — Doconut's standardmäßige Dokumentensicherheit prüft jede Seitenanforderung gegen den ASP.NET‑Sitzungsstatus. Der Sitzungsstatus benötigt einen Cache: AddDistributedMemoryCache() stellt einen bereit. Eine MVC‑ oder Razor‑Pages‑App registriert ihn bereits, aber eine Minimal‑API‑App nicht, und ohne ihn stoppt die App beim Start mit Unable to resolve service for type 'IDistributedCache'. Haben Sie Doconut bereits während der Installation registriert? Springen Sie zum nächsten Abschnitt.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // Der Session-Status benötigt einen Cache (MVC/Razor Pages fügen bereits einen hinzu)
builder.Services.AddSession(); // Doconut-Dokumentsicherheit basiert auf dem ASP.NET Session-Status

app.UseSession();          // Rufen Sie UseSession() vor UseDoconut() auf
app.UseDoconutResources(); // Muss vor UseDoconut() registriert werden
app.UseDoconut();

Für ein produktionsähnliches Pfadlayout ordnen Sie die Dokument‑Middleware einem expliziten Zweig zu und halten die vier Pfadeinstellungen ausgerichtet:

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "Doconut.Viewer.lic");
    options.MiddlewarePath = "/doconut";
    options.ResourcesPath = "/doconut-res";
    options.UnsafeMode = false;
});
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession();

app.UseRouting();
app.UseSession();
app.UseDoconutResources();
app.Map("/doconut", branch => branch.UseDoconut());

MiddlewarePath ist ein Koordinationswert; er mappt keinen ASP.NET‑Core‑Zweig von selbst. In diesem Beispiel mappt der Host /doconut, sodass der Client BasePath: 'doconut' verwendet. ResourcesPath liefert das eingebettete Bundle unter /doconut-res, und der Bild‑Ressourcenpfad des Widgets ist daher ResPath: 'doconut-res/images'. Schreiben Sie beide ohne führenden Schrägstrich: Der Viewer fügt selbst einen hinzu, sodass '/doconut' zu //doconut/DocImage.axd werden würde, was der Browser als Anfrage an einen Host namens doconut interpretiert, und der Viewer schlägt mit "The viewer handler could not be reached" fehl.

Viewer zur Seite hinzufügen

Der Viewer ist der erforderliche Kern der Seite. Seine Rendering‑Oberfläche verwendet zwei verschachtelte divs:

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

Betrachten Sie die Symbolleiste, Modul‑Mounts und die Viewer‑Oberfläche als eine Seitenkomposition. Suche und Annotation fügen ihre eingebetteten Bänder in optionale Mounts ein, aber diese Module stehen niemals für sich allein: Sie werden immer an den Viewer auf derselben Seite angehängt. Verwenden Sie dieselbe Reihenfolge wie Doconut.TestApp und Doconut.TestApp.Distributed:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- Viewer-Navigation, Zoom, Suche und Annotationsschaltflächen -->
</nav>

<div id="searchBarMount"></div>
<div id="annBarMount"></div>

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

Viewer‑Assets referenzieren

In einer Razor‑View gibt der injizierte Viewer‑Dienst die <link>‑ und <script>‑Tags des Viewers in Abhängigkeitsreihenfolge aus — das Widget ist ein jQuery‑Plugin, daher muss jQuery vor den Viewer‑Skripten geladen werden:

html
@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
}))

Für das komplette Viewer‑Paket fordern Sie Viewer‑ und Modul‑Ressourcen gemeinsam an:

html
@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 und IncludeViewerScripts sind die obligatorischen Kern‑Flags. Veröffentlichen Sie niemals ein Such‑ oder Annotations‑Ribbon‑Beispiel ohne sie, den Viewer‑Mount und eine docViewer‑Instanz. ReferenceCss und ReferenceScripts lassen die Ressourcen eines optionalen Moduls weg, wenn die aktuelle Lizenz diese Fähigkeit nicht gewährt; der Kern‑Viewer startet dennoch.

Viewer initialisieren

Das clientseitige Widget ist ein jQuery-Plugin. Dies ist ein minimaler Satz echter Initialisierungsoptionen (kein Pseudocode):

javascript
let searchBar = null;
let annBar = null;

const objViewer = $('#div_ctlDoc').docViewer({
    showThumbs: true,
    autoLoad:   false,
    pageZoom:   100,
    FitType:    'width',
    BasePath:   'doconut',             // kein führender '/'
    ResPath:    'doconut-res/images',  // kein führender '/'
    onViewerReady: function () {
        // Seiten sind sichtbar; hier kann ein Lade‑Spinner sicher ausgeblendet werden
    },
    // Weiterleiten von Annotation-Lebenszyklusereignissen an das eingebettete Ribbon, wenn es vorhanden ist
    onAnnLoaded:    () => annBar?.handleAnnLoaded(),
    onAnnSaved:     () => annBar?.handleAnnSaved(),
    onAnnSaveError: () => annBar?.handleAnnSaveError(),
    onAnnClosed:    () => annBar?.handleAnnClosed(),
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

Die Schreibweise der Optionen ist tatsächlich gemischt — showThumbs, autoLoad und pageZoom sind camelCase, aber FitType, BasePath und ResPath sind PascalCase. Es gibt keine konsistente Regel; bei falscher Schreibweise wird die Option stillschweigend ignoriert (das Widget greift auf seine Standardeinstellung zurück, anstatt einen Fehler zu werfen).

Das komplette Viewer-Paket zusammenstellen

Beide .NET 8-Referenzanwendungen installieren die folgenden Teile zusammen auf einer Seite:

Teil des PaketsAnforderungWie es verbunden ist
Viewer-Ressourcen, Einbindung und objViewerErforderlichKern-Dokumentrenderer
Viewer-WerkzeugleisteErforderlich in der ReferenzzusammensetzungHost-Markup; Buttons rufen dieselbe objViewer auf
SuchleisteOptional, lizenziertes ModuldoconutSearchBar(...).attach(objViewer)
AnmerkungsleisteOptional, lizenziertes ModuldoconutAnnotationBar(...).attach(objViewer)

Obwohl die Haupt-Viewer-Werkzeugleiste Host-Markup ist, wird sie zusammen mit dem Viewer installiert und darf niemals als isoliertes Steuerelement dokumentiert werden. Dadurch bleiben Layout, Beschriftungen, Symbole und Autorisierungsregeln unter der Kontrolle Ihrer Anwendung, während jeder Button dieselbe Viewer-Instanz steuert:

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.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>

Die vollständige Referenz-Werkzeugleiste kopiert außerdem wwwroot/js/viewerToolbar.js in die Host-Anwendung für Drehung, Miniaturansicht, Druck, Vollbild, Layout und Button‑Zustands‑Hilfen. Laden Sie diese Host‑Datei nach Viewer.ReferenceScripts(...). Halten Sie den Helfer und sein <nav id="toolbar">‑Markup zusammen, wenn Sie die vollständige Demo‑Implementierung kopieren.

Behalten Sie die von beiden Referenzanwendungen verwendete Paketinitialisierungsreihenfolge bei:

  1. Viewer-, Such‑ und Anmerkungs‑Ressourcen gemeinsam ausgeben.
  2. Die Viewer‑Werkzeugleiste, Ribbon‑Einbindungen und Viewer‑Einbindung gemeinsam rendern.
  3. docViewer zuerst initialisieren.
  4. Jedes lizenzierte Ribbon erstellen und es an dasselbe objViewer anhängen.
  5. Das Dokument öffnen und sein Token für Modul‑Anfragen behalten.

Doconut.TestApp.Distributed bewahrt diese genaue UI‑Zusammensetzung und denselben Viewer‑Werkzeugleisten‑Helfer. Sein zusätzlicher access‑Anforderungswert und die Einstellungen für asynchrones Render‑Retry gehören zum verteilten Transport; sie ändern nicht, wie Viewer, Werkzeugleiste oder Ribbons zusammengebaut werden.

Die serverseitigen Schutzmechanismen sind wichtig: Wenn eine optionale Fähigkeit nicht verfügbar ist, wird ihr Skript nicht ausgegeben, sodass ihre jQuery‑Plugin‑Funktion nicht existiert.

html
<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>

Beide eingebetteten Komponenten erzeugen ihr eigenes Ribbon‑DOM. Die Suche enthält Gruppen für Finden, Optionen und Ergebnisse. Die Anmerkung enthält ihre Autorentools, Stil‑Steuerelemente, Speicher‑Aktionen und optionale Export‑/Bild‑Aktionen. Die Leisten stellen open(), close(), reset() und isOpen() bereit; rufen Sie stets attach(objViewer) einmal nach deren Erstellung auf.

Das obige Beispiel lässt optionale Host‑Callbacks und Anmerkungs‑Export‑/Bild‑Endpunkte weg, um den Start minimal zu halten. Siehe Suche und Anmerkungen für die vollständige funktionsspezifische Einrichtung, oder Benutzerdefinierte Themen, um die host‑eigene Viewer‑Werkzeugleiste zu stylen oder zu ersetzen.

Dokument öffnen

Auf der Serverseite gibt es einen Endpunkt: Der injizierte Viewer Service öffnet das Dokument und gibt ein Sitzungstoken zurück.

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // Der Token ist undurchsichtig — übergebe ihn dem widget, protokolliere ihn niemals oder speichere ihn nicht.
    string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

Der Client holt dieses Token ab und übergibt es dem Widget mit objViewer.View(token):

javascript
fetch('/api/open', { method: 'POST' })
    .then(resp => resp.json())
    .then(data => {
        currentToken = data.token;
        objViewer.View(currentToken);
    });

Dokument schließen

Rufen Sie objViewer.Close() auf, wenn der Benutzer den Viewer verlässt oder ein Ersatzdokument öffnet. Bei servergesteuerten Workflows entfernt viewer.CloseDocument(token) sofort die zwischengespeicherte Sitzung, gibt die Rendering-Engine frei, löscht dessen Sicherheitsmarker und widerruft das Token. Das gleitende Ablaufdatum führt schließlich dieselbe Bereinigung durch, aber ein explizites Schließen wird für große Dokumente empfohlen.

Der vollständige Anforderungsablauf ist:

text
AddDoconut + middleware
    -> render CSS/scripts and mount div
    -> initialize docViewer
    -> OpenDocumentAsync
    -> return opaque token
    -> objViewer.View(token)
    -> page/search/annotation requests
    -> Close / CloseDocument

Behandeln Sie das Token wie ein Bearer‑Credential: Loggen Sie es nie, speichern Sie es nie, übergeben Sie es nur an das Widget. Es identifiziert eine aktive Dokumentsitzung auf dem Server und hört auf zu funktionieren, wenn diese Sitzung abläuft – öffnen Sie das Dokument erneut, um ein frisches Token zu erhalten.

Ausführen

Legen Sie ein PDF unter wwwroot/files/Sample.pdf ab, führen Sie dotnet run aus und öffnen Sie die Seite, die das Widget hostet. Die erste Seite wird im Viewer angezeigt, mit einem Miniatur‑Panel links. Wenn dies nicht geschieht, siehe Fehlerbehebung.

Was Sie ohne Lizenz erhalten

Eine fehlende Lizenz löst keinen Fehler aus. Der Viewer rendert normal, aber jede Seite trägt ein Evaluations‑Wasserzeichen. Siehe Lizenz‑Einrichtung für Informationen, wie Doconut eine Lizenz findet und was sich ändert, sobald sie gefunden wurde.

War diese Seite hilfreich?