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 renderizzato nel browser: registrazione del server, il pacchetto completo del Viewer (barra degli strumenti del Viewer, montaggio del Viewer e nastri opzionali di Ricerca/Annotazione), riferimenti alle risorse, inizializzazione del client, apertura del documento e esecuzione.
L'applicazione host deve puntare a un runtime che supporti netstandard2.1 — .NET Core 3.0 o versioni successive. Le applicazioni di riferimento fornite con questa versione puntano a net8.0.
Configurazione del server
AddDoconut() registra i servizi; UseDoconutResources() e UseDoconut() collegano il middleware. La chiamata alle risorse deve venire prima. Le chiamate alla 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 l'Installazione? Passa alla sezione successiva.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // lo stato della sessione richiede una cache (MVC/Razor Pages ne aggiungono già una)
builder.Services.AddSession(); // la sicurezza dei documenti Doconut si basa sullo stato della sessione ASP.NET
app.UseSession(); // chiamare UseSession() prima di UseDoconut()
app.UseDoconutResources(); // deve essere registrato prima di UseDoconut()
app.UseDoconut();L'applicazione di riferimento mantiene la forma storica della richiesta DocImage.axd e monta il middleware del documento su un ramo corrispondente. Questa è la configurazione da copiare quando si porta avanti un'applicazione .NET Standard esistente, poiché il client continua a chiamare la stessa URL come sempre:
builder.Services.AddDoconut(options =>
{
options.UnsafeMode = false; // mantieni abilitato il controllo della sessione di Doconut
options.ShowDoconutInfo = false;
});
builder.Services.AddDistributedMemoryCache(); // lo stato della sessione richiede una cache (MVC/Razor Pages ne aggiungono già una)
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(3600);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// Necessario per codifiche multi-byte in alcuni formati di documento.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseDoconutResources(); // PRIMA del ramo del documento
app.MapWhen(
context => context.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
branch => branch.UseDoconut());UseDoconut() non accetta opzioni in questa versione — tutto è stato spostato in AddDoconut(). MiddlewarePath è un valore di coordinamento; non mappa un ramo ASP.NET Core da solo. ResourcesPath serve il bundle incorporato su /doconut-res, e il percorso delle risorse immagine del widget è quindi ResPath: 'doconut-res/images'.
Aggiungi il visualizzatore a una pagina
Il Viewer è il nucleo richiesto della pagina. La sua superficie di rendering utilizza due div annidati:
<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 inseriscono i loro nastri incorporati nei montaggi opzionali, ma questi moduli non sono mai autonomi: si collegano sempre al Viewer nella stessa pagina.
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Navigazione del Viewer, zoom, pulsanti di Ricerca e Annotazione -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Riferimento alle 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 del 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 Ribbon 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.
Queste chiamate sostituiscono i file script copiati manualmente. Se l'applicazione carica ancora i propri docViewer.js, documentLinks.js o docViewer.UI.js, rimuovi quei tag una volta che la pagina funziona con le risorse emesse — vedi la guida alla migrazione.
Inizializzare 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: '/',
ResPath: 'doconut-res/images',
onViewerReady: function () {
// le pagine sono visibili; è sicuro nascondere qui lo spinner di caricamento
},
// Inoltra gli eventi del ciclo di vita dell'annotazione 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);
}
});Due dettagli determinano se questa pagina funziona affatto:
- I callback sono opzioni.
onViewerReady,onViewerBusy,onCopyeonAutoLoadStatusvengono passati nell'oggetto delle opzioni. Il visualizzatore precedente derivava i nomi delle funzioni globali dall'ID del contenitore e le chiamava; questo non lo fa. Un gestore dichiarato solo come funzione globale non viene mai eseguito e non viene segnalato nulla. ResPathindica il prefisso delle risorse, non la radice dell'applicazione. Con il valore predefinitoResourcesPath, questo valore èdoconut-res/images. Se hai modificatoResourcesPath, usa il tuo prefisso.
Entrambi i casi falliscono silenziosamente — vedi la Risoluzione dei problemi e la guida alla migrazione.
La capitalizzazione delle opzioni è davvero mista — showThumbs, autoLoad e pageZoom sono camelCase, mentre FitType, BasePath e ResPath sono PascalCase. Non esiste una regola coerente; se la capitalizzazione è errata l'opzione viene ignorata silenziosamente (il widget ritorna al valore predefinito invece di generare un errore).
Assemblare il pacchetto completo del Viewer
Entrambe le applicazioni di riferimento installano le seguenti parti insieme in una pagina:
| Parte del pacchetto | Requisito | Come è collegata |
|---|---|---|
Risorse del Viewer, montaggio 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 |
| Ribbon di ricerca | Opzionale, modulo con licenza | doconutSearchBar(...).attach(objViewer) |
| Ribbon di annotazione | Opzionale, modulo con licenza | doconutAnnotationBar(...).attach(objViewer) |
Anche se la barra degli strumenti principale del Viewer è 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 controlla 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>Mantieni l'ordine di inizializzazione del pacchetto usato da entrambe le applicazioni di riferimento:
- Emettere CSS per il Viewer e i moduli con licenza.
- Renderizzare la barra degli strumenti del Viewer, i montaggi di Ricerca/Annotazione e il montaggio del Viewer insieme.
- Emettere script per il Viewer e i moduli con licenza.
- Caricare lo script di supporto della barra degli strumenti dell'applicazione host.
- Inizializzare
docViewere conservare il risultatoobjViewer. - Inizializzare ogni Ribbon di Ricerca o Annotazione con licenza.
- Chiamare
attach(objViewer)su ogni Ribbon. - Aprire il documento e conservare il suo token per le richieste del Viewer e dei moduli.
I controlli lato server sono importanti: quando una capacità opzionale non è disponibile, il suo script non viene emesso, quindi la funzione del 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. La ricerca contiene i gruppi Trova, Opzioni e Risultati. L'annotazione 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 dell'Annotazione per mantenere l'avvio il più minimale possibile. Vedi Ricerca e Annotazioni per la configurazione completa delle funzionalità, o Temi personalizzati per stilizzare o sostituire la barra degli strumenti del Viewer gestita dall'host.
Apri un documento
Il lato server è un unico endpoint: il servizio Viewer iniettato apre il documento e restituisce un token di sessione. L'apertura è asincrona in questa versione.
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);
});Non c'è nulla da preparare prima della prima apertura. Se stai aggiornando un'applicazione, elimina ogni chiamata a InitCache() e la relativa flag — il metodo non esiste più.
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. La scadenza 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, passalo 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 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 quali cambiamenti avvengono una volta trovata.
Questa pagina è stata utile?