Guida Rapida

Visualizza il tuo primo documento in pochi minuti

Questa guida porta un'app ASP.NET Core da un Program.cs vuoto a un documento visualizzato nel browser: registrazione del server, il pacchetto completo Viewer (barra degli strumenti Viewer, montaggio Viewer e nastri opzionali di Ricerca/Annotazione), riferimenti alle risorse, inizializzazione del client, apertura del documento e esecuzione.

Configurazione del server

AddDoconut() registra i servizi; UseDoconutResources() e UseDoconut() collegano il middleware. La chiamata alle risorse deve venire per prima. Le chiamate di sessione sono necessarie anch'esse — la sicurezza predefinita dei documenti Doconut convalida ogni richiesta di pagina contro lo stato della sessione ASP.NET. Lo stato della sessione richiede una cache: AddDistributedMemoryCache() ne fornisce una. Un'app MVC o Razor Pages la registra già, ma un'app minimal-API no, e senza di essa l'app si arresta all'avvio con Unable to resolve service for type 'IDistributedCache'. Hai già registrato Doconut durante Installazione? Passa alla sezione successiva.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // lo stato della sessione richiede una cache (MVC/Razor Pages già ne aggiungono una)
builder.Services.AddSession(); // la sicurezza dei documenti Doconut si basa sullo stato della sessione ASP.NET

app.UseSession();          // chiama UseSession() prima di UseDoconut()
app.UseDoconutResources(); // deve essere registrato prima di UseDoconut()
app.UseDoconut();

Per una struttura dei percorsi in stile produzione, mappa il middleware dei documenti a un ramo esplicito e mantieni allineate le quattro impostazioni dei percorsi:

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 è un valore di coordinamento; non mappa un ramo ASP.NET Core da solo. In questo esempio l'host mappa /doconut, quindi il client usa BasePath: 'doconut'. ResourcesPath serve il bundle incorporato su /doconut-res, e il percorso delle risorse immagine del widget è quindi ResPath: 'doconut-res/images'. Scrivi entrambi senza una barra iniziale: il visualizzatore ne aggiunge una automaticamente, così '/doconut' diventerebbe //doconut/DocImage.axd, che il browser interpreta come una richiesta a un host chiamato doconut, e il visualizzatore fallisce con "Il gestore del visualizzatore non è stato raggiunto".

Aggiungi il visualizzatore a una pagina

Il Viewer è il nucleo richiesto della pagina. La sua superficie di rendering utilizza due div annidati:

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

Considera la barra degli strumenti, i montaggi dei moduli e la superficie del Viewer come un'unica composizione di pagina. Ricerca e Annotazione iniettano i loro nastri incorporati nei montaggi opzionali, ma questi moduli non sono mai autonomi: si collegano sempre al Viewer nella stessa pagina. Usa lo stesso ordine di Doconut.TestApp e Doconut.TestApp.Distributed:

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>

Riferisci le risorse del visualizzatore

In una vista Razor, il servizio Viewer iniettato genera i tag <link> e <script> del visualizzatore in ordine di dipendenza — il widget è un plugin jQuery, quindi jQuery deve essere caricato prima degli script del visualizzatore:

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

Per il pacchetto completo del Viewer, richiedi insieme le risorse del Viewer e dei moduli:

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 e IncludeViewerScripts sono i flag obbligatori del nucleo. Non pubblicare mai un esempio di Nastro di Ricerca o Annotazione senza di essi, il montaggio del Viewer e un'istanza docViewer. ReferenceCss e ReferenceScripts omettono le risorse di un modulo opzionale quando la licenza corrente non concede tale capacità; il Viewer di base si avvia comunque.

Inizializza il visualizzatore

Il widget lato client è un plugin jQuery. Questo è un set minimo di opzioni di inizializzazione reali (non pseudocodice):

javascript
let searchBar = null;
let annBar = null;

const objViewer = $('#div_ctlDoc').docViewer({
    showThumbs: true,
    autoLoad:   false,
    pageZoom:   100,
    FitType:    'width',
    BasePath:   'doconut',             // nessun '/' iniziale
    ResPath:    'doconut-res/images',  // nessun '/' iniziale
    onViewerReady: function () {
        // le pagine sono visibili; è sicuro nascondere qui lo spinner di caricamento
    },
    // Inoltra gli eventi del ciclo di vita delle annotazioni al ribbon incorporato quando è presente.
    onAnnLoaded:    () => annBar?.handleAnnLoaded(),
    onAnnSaved:     () => annBar?.handleAnnSaved(),
    onAnnSaveError: () => annBar?.handleAnnSaveError(),
    onAnnClosed:    () => annBar?.handleAnnClosed(),
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

Il casing delle opzioni è davvero misto — showThumbs, autoLoad e pageZoom sono camelCase, ma FitType, BasePath e ResPath sono PascalCase. Non esiste una regola coerente; se il casing è sbagliato l'opzione viene silenziosamente ignorata (il widget ricade sul valore predefinito invece di lanciare un errore).

Assembla il pacchetto completo del Visualizzatore

Entrambe le applicazioni di riferimento .NET 8 installano le seguenti parti insieme su una pagina:

Parte del pacchettoRequisitoCome è collegata
Risorse del visualizzatore, mount e objViewerObbligatorioRenderer di documento core
Barra degli strumenti del visualizzatoreObbligatorio nella composizione di riferimentoMarkup host; i pulsanti chiamano lo stesso objViewer
Barra di ricercaOpzionale, modulo con licenzadoconutSearchBar(...).attach(objViewer)
Barra di annotazioneOpzionale, modulo con licenzadoconutAnnotationBar(...).attach(objViewer)

Sebbene la barra degli strumenti principale del Visualizzatore sia markup host, viene installata insieme al Visualizzatore e non deve mai essere documentata come un controllo isolato. Questo mantiene il layout, le etichette, le icone e le regole di autorizzazione sotto il controllo della tua applicazione, mentre ogni pulsante agisce sulla stessa istanza del Visualizzatore:

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>

La barra di riferimento completa copia anche wwwroot/js/viewerToolbar.js nell'applicazione host per rotazione, miniature, stampa, schermo intero, layout e helper per lo stato dei pulsanti. Carica quel file host dopo Viewer.ReferenceScripts(...). Mantieni l'helper e il markup <nav id="toolbar"> insieme quando copi l'implementazione demo completa.

Mantieni l'ordine di inizializzazione del pacchetto usato da entrambe le applicazioni di riferimento:

  1. Emettere le risorse del Visualizzatore, della Ricerca e dell'Annotazione insieme.
  2. Renderizzare la barra degli strumenti del Visualizzatore, i mount delle Ribbon e il mount del Visualizzatore insieme.
  3. Inizializzare prima docViewer.
  4. Creare ogni Ribbon con licenza e collegarla allo stesso objViewer.
  5. Aprire il documento e conservare il suo token per le richieste dei moduli.

Doconut.TestApp.Distributed mantiene questa esatta composizione UI e lo stesso helper della barra degli strumenti del Visualizzatore. Il suo valore di richiesta access aggiuntivo e le impostazioni di retry di rendering asincrono appartengono al trasporto distribuito; non modificano il modo in cui il Visualizzatore, la barra degli strumenti o le Ribbon sono assemblati.

Le guardie lato server sono importanti: quando una capacità opzionale non è disponibile, il suo script non viene emesso, quindi la funzione plugin jQuery non esiste.

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>

Entrambi i componenti incorporati generano il proprio DOM Ribbon. La barra di ricerca contiene i gruppi Trova, Opzioni e Risultati. La barra di annotazione contiene gli strumenti di authoring, i controlli di stile, le azioni di salvataggio e le azioni opzionali di esportazione/immagine. Le barre espongono open(), close(), reset() e isOpen(); è sempre necessario chiamare attach(objViewer) una volta dopo la loro creazione.

L'esempio sopra omette callback host opzionali e endpoint di esportazione/immagine dell'Annotazione per mantenere l'avvio minimale. Vedi Ricerca e Annotazioni per la configurazione completa delle funzionalità specifiche, o Temi personalizzati per stilizzare o sostituire la barra degli strumenti del Visualizzatore di proprietà dell'host.

Apri un documento

Il lato server è un unico endpoint: il servizio Viewer iniettato apre il documento e restituisce un token di sessione.

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // Il token è opaco — consegnalo al widget, non registrarlo né conservarlo.
    string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

Il client recupera quel token e lo passa al widget con objViewer.View(token):

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

Chiudi il documento

Chiama objViewer.Close() quando l'utente lascia il visualizzatore o apre un documento di sostituzione. Nei flussi di lavoro guidati dal server, viewer.CloseDocument(token) rimuove immediatamente la sessione nella cache, rilascia il motore di rendering, elimina il suo marcatore di sicurezza e revoca il token. L'espirazione scorrevole alla fine esegue la stessa pulizia, ma la chiusura esplicita è consigliata per documenti di grandi dimensioni.

Il flusso di richiesta completato è:

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

Tratta il token come una credenziale di tipo bearer: non registrarlo mai, non conservarlo mai, consegnalo solo al widget. Identifica una sessione di documento attiva sul server e smette di funzionare quando quella sessione scade — riapri il documento per ottenerne uno nuovo.

Eseguilo

Posiziona un PDF in wwwroot/files/Sample.pdf, esegui dotnet run e apri la pagina che ospita il widget. La prima pagina viene renderizzata nel visualizzatore, con un pannello di miniature a sinistra. Se non accade, consulta Risoluzione dei problemi.

Cosa ottieni senza licenza

Una licenza mancante non genera eccezioni. Il visualizzatore rende normalmente, ma ogni pagina porta una filigrana di valutazione. Consulta Configurazione della licenza per capire come Doconut trova una licenza e cosa cambia una volta trovata.

Questa pagina è stata utile?