Snabbstart

Rendera ditt första dokument på några minuter

Denna genomgång tar en ASP.NET Core-app från en tom Program.cs till ett dokument som renderas i webbläsaren: serverregistrering, det kompletta Viewer‑paketet (Viewer‑verktygsfält, Viewer‑montering och valfria sök‑/annotationsband), tillgångsreferenser, klientinitialisering, dokumentöppning och körning.

Värdapplikationen måste rikta sig mot en runtime som stödjer netstandard2.1 — .NET Core 3.0 eller senare. Referensapplikationerna som levereras med denna version riktar sig mot net8.0.

Serverinställning

AddDoconut() registrerar tjänsterna; UseDoconutResources() och UseDoconut() kopplar middleware. Resursanropet måste komma först. Sessionsanropen krävs också — Doco­coconut's standarddokument‑säkerhet validerar varje sidförfrågan mot ASP.NET‑sessionsstatus. Sessionsstatus behöver en cache: AddDistributedMemoryCache() tillhandahåller en. En MVC‑ eller Razor‑Pages‑app registrerar den redan, men en minimal‑API‑app gör det inte, och utan den stannar appen vid start med Unable to resolve service for type 'IDistributedCache'. Har du redan registrerat Doconut under Installation? Hoppa över till nästa avsnitt.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // sessionsstatus behöver en cache (MVC/Razor Pages lägger redan till en)
builder.Services.AddSession(); // Doconut-dokumentsäkerhet bygger på ASP.NET sessionsstatus

app.UseSession();          // anropa UseSession() före UseDoconut()
app.UseDoconutResources(); // måste registreras före UseDoconut()
app.UseDoconut();

Referensapplikationen behåller den historiska DocImage.axd‑förfrågningsformen och monterar dokument‑middleware på en matchande gren. Detta är arrangemanget att kopiera när du vidareutvecklar en befintlig .NET Standard-applikation, eftersom klienten fortsätter att anropa samma URL som den alltid gjort:

csharp
builder.Services.AddDoconut(options =>
{
    options.UnsafeMode      = false;   // behåll Doconut:s sessionskontroll aktiverad
    options.ShowDoconutInfo = false;
});
builder.Services.AddDistributedMemoryCache(); // sessionsstatus behöver en cache (MVC/Razor Pages lägger redan till en)
builder.Services.AddSession(options =>
{
    options.IdleTimeout        = TimeSpan.FromSeconds(3600);
    options.Cookie.HttpOnly    = true;
    options.Cookie.IsEssential = true;
});

// Krävs för multibyte‑kodningar i vissa dokumentformat.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseDoconutResources();   // INNAN dokumentgrenen
app.MapWhen(
    context => context.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
    branch => branch.UseDoconut());

UseDoconut() tar inga alternativ i denna version — allt har flyttats till AddDoconut(). MiddlewarePath är ett koordineringsvärde; det mappar inte en ASP.NET Core‑gren i sig. ResourcesPath serverar det inbäddade paketet på /doconut-res, och widgetens bildresurs‑sökväg är därför ResPath: 'doconut-res/images'.

Lägg till visaren på en sida

Viewern är den nödvändiga kärnan på sidan. Dess renderingsyta använder två nästlade divs:

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

Behandla verktygsfältet, modulmonteringar och Viewer‑ytan som en enhetlig sidkomposition. Sök och Annotation injicerar sina inbäddade band i valfria monteringar, men dessa moduler är aldrig fristående: de fäster alltid på Viewer på samma sida.

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- Viewer navigation, zoom, Search, and Annotation buttons -->
</nav>

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

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

Referera till Viewer‑resurserna

I en Razor‑vy injicerar Viewer‑tjänsten <link>‑ och <script>‑taggarna för visaren i beroendeordning — widgeten är ett jQuery‑plugin, så jQuery måste laddas innan viewer‑skripten:

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 hela Viewer‑paketet, begär Viewer‑ och modulresurserna tillsammans:

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 och IncludeViewerScripts är de obligatoriska kärnflaggan. Publicera aldrig ett Search‑ eller Annotation‑Ribbon‑exempel utan dem, Viewer‑monteringen och en docViewer‑instans. ReferenceCss och ReferenceScripts utelämnar ett valfritt moduls resurser när den aktuella licensen inte ger den möjligheten; kärn‑Viewer startar ändå.

De här anropen ersätter handkopierade skriptfiler. Om applikationen fortfarande laddar sina egna docViewer.js, documentLinks.js eller docViewer.UI.js, ta bort de taggarna när sidan fungerar med de utsända resurserna — se migrationsguide.

Initiera Viewer

Klient‑sidans widget är ett jQuery‑plugin. Detta är en minimal uppsättning faktiska initieringsalternativ (inte pseudokod):

javascript
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 () {
        // sidorna är synliga; säkert att dölja en laddningsspinner här
    },
    // Skicka vidare annoteringslivscykelhändelser till den inbäddade ribbonen när den är närvarande.
    onAnnLoaded:    () => annBar?.handleAnnLoaded(),
    onAnnSaved:     () => annBar?.handleAnnSaved(),
    onAnnSaveError: () => annBar?.handleAnnSaveError(),
    onAnnClosed:    () => annBar?.handleAnnClosed(),
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

Två detaljer avgör om den här sidan fungerar alls:

  • Callback‑funktioner är alternativ. onViewerReady, onViewerBusy, onCopy och onAutoLoadStatus skickas in i options‑objektet. Den tidigare viewern härledde globala funktionsnamn från container‑id:t och anropade dem; den här gör det inte. En hanterare som bara deklareras som en global funktion körs aldrig, och inget rapporteras.
  • ResPath pekar på resurs‑prefixet, inte applikationsroten. Med standard‑ResourcesPath är värdet doconut-res/images. Om du har ändrat ResourcesPath, använd ditt eget prefix istället.

Båda dessa misslyckas tyst — se Felsökning och migrationsguide.

Alternativens skiftning är faktiskt blandad — showThumbs, autoLoad och pageZoom är camelCase, men FitType, BasePath och ResPath är PascalCase. Det finns ingen konsekvent regel; om du får skiftningen fel ignoreras alternativet tyst (widgeten återgår till sitt standardvärde istället för att kasta ett fel).

Sätt ihop hela Viewer‑paketet

Båda referensapplikationerna installerar följande delar tillsammans på en sida:

Del av paketetKravHur den är ansluten
Viewer‑resurser, montering och objViewerKrävsKärndokumentrenderare
Viewer‑verktygsfältKrävs i referenskompositionenVärd‑markup; knappar anropar samma objViewer
Search‑ribbonValfri, licensierad moduldoconutSearchBar(...).attach(objViewer)
Annotation‑ribbonValfri, licensierad moduldoconutAnnotationBar(...).attach(objViewer)

Även om huvud‑Viewer‑verktygsfältet är värd‑markup, installeras det tillsammans med Viewer och får aldrig dokumenteras som en fristående kontroll. Detta håller dess layout, etiketter, ikoner och behörighetsregler under din applikations kontroll samtidigt som varje knapp styr samma Viewer‑instans:

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>

Behåll paketets initieringsordning som används av båda referensapplikationerna:

  1. Utsänd CSS för Viewer och licensierade moduler.
  2. Rendera Viewer‑verktygsfältet, Search/Annotation‑monteringar och Viewer‑monteringen tillsammans.
  3. Utsänd skript för Viewer och licensierade moduler.
  4. Ladda värd‑applikationens eget verktygsfält‑hjälpskript.
  5. Initiera docViewer och behåll den resulterande objViewer.
  6. Initiera varje licensierad Search‑ eller Annotation‑Ribbon.
  7. Anropa attach(objViewer) på varje Ribbon.
  8. Öppna dokumentet och behåll dess token för Viewer‑ och modul‑förfrågningar.

Server‑sidans skydd är viktiga: när en valfri funktionalitet inte är tillgänglig, utsänds dess skript inte, så dess jQuery‑plugin‑funktion finns inte.

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>

Båda inbäddade komponenterna genererar sin egen Ribbon‑DOM. Search innehåller Find, Options och Results‑grupper. Annotation innehåller sina författarverktyg, stilkontroller, spara‑åtgärder och valfria export‑/bild‑åtgärder. Balkarna exponerar open(), close(), reset() och isOpen(); anropa alltid attach(objViewer) en gång efter att de skapats.

Exemplet ovan utelämnar valfria värd‑callback‑funktioner och Annotation‑export‑/bild‑slutpunkter för att hålla uppstarten minimal. Se Sök och Annotationer för den kompletta funktionsspecifika konfigurationen, eller Anpassade teman för att styla eller ersätta det värd‑ägda Viewer‑verktygsfältet.

Öppna ett dokument

Server‑sidan har en enda endpoint: den injicerade Viewer‑tjänsten öppnar dokumentet och returnerar en sessionstoken. Öppning är asynkron i denna version.

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // Token är opak — överlämna den till widgeten, logga den aldrig eller lagra den.
    string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

Klienten hämtar den token och överlämnar den till widgeten med objViewer.View(token):

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

Det finns inget att förbereda innan den första öppningen. Om du vidareutvecklar en applikation, ta bort alla anrop till InitCache() och flaggan som skyddar den — metoden finns inte längre.

Stäng dokumentet

Anropa objViewer.Close() när användaren lämnar visaren eller öppnar ett ersättningsdokument. I serverstyrda arbetsflöden tar viewer.CloseDocument(token) omedelbart bort den cachade sessionen, frigör renderingsmotorn, raderar dess säkerhetsmarkör och återkallar token. Glidande utgång utför så småningom samma städning, men explicit stängning rekommenderas för stora dokument.

Det fullständiga begärandeflödet är:

text
AddDoconut + middleware
    -> rendera CSS/skript och montera div
    -> initiera docViewer
    -> OpenDocumentAsync
    -> returnera opak token
    -> objViewer.View(token)
    -> sid-/sök-/annotationsförfrågningar
    -> Close / CloseDocument

Behandla token som en bärar‑behörighet: logga den aldrig, lagra den aldrig, överlämna den endast till widgeten. Den identifierar en aktiv dokumentsession på servern och slutar fungera när den sessionen löper ut — öppna dokumentet igen för att få en ny.

Kör det

Placera en PDF i wwwroot/files/Sample.pdf, kör dotnet run och öppna sidan som hostar widgeten. Den första sidan renderas i visaren, med en miniatyrpanel till vänster. Om den inte gör det, se Felsökning.

Vad du får utan licens

En saknad licens ger inget fel. Visaren renderar normalt, men varje sida har ett utvärderingsvattenmärke. Se Licensinställning för hur Doconut hittar en licens och vad som förändras när den gör det.

Var den här sidan hjälpsam?