Avvio Rapido
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 alla sessione sono richieste anche — la sicurezza predefinita dei documenti di Doconut convalida ogni richiesta di pagina rispetto allo stato della sessione ASP.NET. Lo stato della sessione necessita di 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 Installation? Salta alla sezione successiva.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // lo stato di sessione richiede una cache (MVC/Razor Pages ne aggiungono già una)
builder.Services.AddSession(); // La sicurezza dei documenti Doconut si basa sullo stato di 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 di percorso:
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 coordinazione; non mappa un ramo ASP.NET Core da solo. In questo esempio l'host mappa /doconut, quindi il client utilizza BasePath: 'doconut'. ResourcesPath serve il bundle incorporato su /doconut-res, e il percorso della risorsa 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 "The viewer handler could not be reached".
Aggiungi il visualizzatore a una pagina
Il Viewer è il nucleo richiesto della pagina. La sua superficie di rendering utilizza due div nidificati:
<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:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Pulsanti di navigazione, zoom, Ricerca e Annotazione del visualizzatore -->
</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 emette 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:
@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 Viewer, richiedi le risorse del Viewer e dei moduli insieme:
@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 le flag di base obbligatorie. Non pubblicare mai un
esempio di Nastro di Ricerca o Annotazione senza di esse, 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):
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 un indicatore 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);
}
});La capitalizzazione delle opzioni è davvero mista — showThumbs, autoLoad e pageZoom sono camelCase, ma FitType, BasePath e ResPath sono PascalCase. Non esiste una regola coerente; se la capitalizzazione è errata l'opzione viene silenziosamente ignorata (il widget torna al valore predefinito invece di generare un errore).
Assembla il pacchetto completo del Viewer
Entrambe le applicazioni di riferimento .NET 6 installano le seguenti parti insieme su una singola pagina:
| Parte del pacchetto | Requisito | Come è collegato |
|---|---|---|
Risorse del Viewer, mount e objViewer | Obbligatorio | Renderer di documento core |
| Barra degli strumenti del Viewer | Obbligatorio nella composizione di riferimento | Markup host; i pulsanti chiamano lo stesso objViewer |
| Barra di ricerca | Opzionale, modulo con licenza | doconutSearchBar(...).attach(objViewer) |
| Barra di annotazione | Opzionale, modulo con licenza | doconutAnnotationBar(...).attach(objViewer) |
Sebbene la barra degli strumenti principale del Viewer sia markup host, viene installata insieme al Viewer e non deve mai essere documentata come un controllo isolato. Questo mantiene il suo layout, le etichette, le icone e le regole di autorizzazione sotto il controllo della tua applicazione, mentre ogni pulsante guida la stessa istanza del 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>La barra degli strumenti di riferimento completa copia anche wwwroot/js/viewerToolbar.js nell'applicazione host per la rotazione, le miniature, la stampa, lo schermo intero, il layout e gli helper di stato dei pulsanti. Carica quel file host dopo Viewer.ReferenceScripts(...). Mantieni l'helper e il suo markup <nav id="toolbar"> insieme quando copi l'implementazione completa della demo.
Mantieni l'ordine di inizializzazione del pacchetto usato da entrambe le applicazioni di riferimento:
- Emetti CSS per il Viewer e i moduli con licenza.
- Renderizza la barra degli strumenti del Viewer, i mount di Search/Annotation e il mount del Viewer insieme.
- Emetti script per il Viewer e i moduli con licenza.
- Carica
viewerToolbar.jsdell'applicazione host. - Inizializza
docViewere conserva il risultatoobjViewer. - Inizializza ogni Ribbon di Search o Annotation con licenza.
- Chiama
attach(objViewer)su ogni Ribbon. - Apri il documento e conserva il suo token per le richieste del Viewer e dei moduli.
Doconut.TestApp.Distributed mantiene questa esatta composizione UI e lo stesso helper della barra degli strumenti del Viewer. Il suo valore di richiesta aggiuntivo access e le impostazioni di retry del rendering asincrono appartengono al trasporto distribuito; non modificano il modo in cui il Viewer, la barra degli strumenti o i Ribbon vengono assemblati.
Le guardie lato server sono importanti: quando una funzionalità opzionale non è disponibile, il suo script non viene emesso, quindi la sua funzione plugin jQuery non esiste.
<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 del Ribbon. Search contiene i gruppi Find, Options e Results. Annotation contiene i suoi 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(); chiama sempre attach(objViewer) una volta dopo averle create.
L'esempio sopra omette i callback host opzionali e gli endpoint di esportazione/immagine di Annotation per mantenere l'avvio minimale. Vedi Search e Annotations per la configurazione completa specifica delle funzionalità, oppure Custom Themes per stilizzare o sostituire la barra degli strumenti del Viewer 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.
app.MapPost("/api/open", async (Viewer viewer) =>
{
// Il token è opaco — passalo 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):
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 esce dal visualizzatore o apre un documento di sostituzione. Nei flussi di lavoro guidati dal server, viewer.CloseDocument(token) rimuove immediatamente la sessione nella cache, elimina il motore di rendering, cancella 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 è:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentTratta 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 lo fa, consulta Risoluzione dei problemi.
Cosa ottieni senza licenza
Una licenza mancante non genera errori. Il visualizzatore rende normalmente, ma ogni pagina presenta 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?