Snabbstart

Render your first document in minutes

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.

Serverkonfiguration

AddDoconut() registrerar tjänsterna; UseDoconutResources() och UseDoconut() kopplar middleware. Resursanropet måste komma först. Sessionanropen krävs också — Doconut:s standarddokumentssäkerhet validerar varje sidförfrågan mot ASP.NET‑sessionsstatus. Sessionsstatusen 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 vidare 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 för vidare 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();   // BEFORE 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 samordningsvärde; det mappar inte en ASP.NET Core‑gren av sig själv. ResourcesPath levererar 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

Viewer ä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, modulmonteringarna och Viewer‑ytan som en enda sidkomposition. Sök och Annotation injicerar sina inbäddade band i valfria monteringar, men dessa moduler är aldrig fristående: de fästs alltid på Viewer på samma sida.

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- Viewer‑navigering, zoom, sökning och annoteringsknappar -->
</nav>

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

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

Referera till visningsresurserna

I en Razor‑vy injiceras Viewer‑tjänsten som avger visnings‑<link> och <script>‑taggar i beroendeordning — widgeten är ett jQuery‑plugin, så jQuery måste laddas innan visnings‑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 det kompletta 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ärnflaggorna. Publicera aldrig ett exempel på Search‑ eller Annotation‑Ribbon 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å.

Dessa anrop ersätter handkopierade skriptfiler. Om applikationen fortfarande laddar sina egna docViewer.js, documentLinks.js eller docViewer.UI.js, ta bort dessa taggar när sidan fungerar med de utsända resurserna — se migrationsguiden.

Initiera Viewer

Klient‑sidans widget är ett jQuery‑plugin. Detta är en minimal uppsättning faktiska init‑alternativ (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 () {
        // sidor är synliga; det är 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:

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

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

Alternativens versalisering ä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 versaliseringen fel ignoreras alternativet tyst (widgeten återgår till sitt standardvärde istället för att kasta ett fel).

Sätt ihop det kompletta 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ärndokument‑renderare
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 auktoriseringsregler 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. Generera CSS för Viewer och licensierade moduler.
  2. Rendera Viewer‑verktygsfältet, Search/Annotation‑monteringar och Viewer‑monteringen tillsammans.
  3. Generera 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, genereras dess skript inte, så dess jQuery‑plugin‑funktion existerar 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. Stängerna 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 Search och Annotations för den kompletta funktionsspecifika konfigurationen, eller Custom Themes för att styla eller ersätta det värd‑ägda Viewer‑verktygsfältet.

Öppna ett dokument

Server‑sidan är en enda slutpunkt: den injicerade Viewer‑tjänsten öppnar dokumentet och returnerar en sessionstoken. Öppning är asynkron i den här releasen.

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // Token är opak — ge den till widgeten, logga eller spara den aldrig.
    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 InitCache()‑anrop och flaggan som skyddar dem — 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.

Den fullständiga begäranflödet är:

text
AddDoconut + middleware
    -> render CSS/scripts and mount div
    -> initialize docViewer
    -> OpenDocumentAsync
    -> return opaque token
    -> objViewer.View(token)
    -> page/search/annotation requests
    -> 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 på wwwroot/files/Sample.pdf, kör dotnet run och öppna sidan som innehåller 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 kastar inte ett fel. Visaren renderar normalt, men varje sida har ett utvärderingsvattenstämpel. 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?