Inicio rápido
Renderiza tu primer documento en minutos
Esta guía lleva una aplicación ASP.NET Core desde un Program.cs vacío hasta un documento renderizado en el navegador: registro del servidor, el paquete completo del Viewer (barra de herramientas del Viewer, montaje del Viewer y cintas opcionales de Búsqueda/Anotación), referencias de recursos, inicialización del cliente, apertura del documento y ejecución.
Configuración del servidor
AddDoconut() registra los servicios; UseDoconutResources() y UseDoconut() conectan el middleware. La llamada a los recursos debe ir primero. Las llamadas a la sesión también son necesarias — la seguridad de documentos Doconut valida cada solicitud de página contra el estado de sesión de ASP.NET. El estado de sesión necesita una caché: AddDistributedMemoryCache() proporciona una. Una aplicación MVC o Razor Pages ya la registra, pero una aplicación minimal-API no lo hace, y sin ella la aplicación se detiene al iniciar con Unable to resolve service for type 'IDistributedCache'. ¿Ya registraste Doconut durante la Instalación? Avanza a la siguiente sección.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // el estado de sesión necesita una caché (MVC/Razor Pages ya añaden una)
builder.Services.AddSession(); // La seguridad de documentos Doconut se basa en el estado de sesión de ASP.NET
app.UseSession(); // llame a UseSession() antes de UseDoconut()
app.UseDoconutResources(); // debe registrarse antes de UseDoconut()
app.UseDoconut();Para una estructura de rutas estilo producción, mapee el middleware de documentos a una rama explícita y mantenga alineadas las cuatro configuraciones de ruta:
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 es un valor de coordinación; no mapea una rama de ASP.NET Core por sí mismo. En este ejemplo el host mapea /doconut, por lo que el cliente usa BasePath: 'doconut'. ResourcesPath sirve el paquete incrustado en /doconut-res, y la ruta de recursos de imágenes del widget es por lo tanto ResPath: 'doconut-res/images'. Escriba ambos sin una barra inicial: el Viewer agrega una por sí mismo, de modo que '/doconut' se convertiría en //doconut/DocImage.axd, que el navegador interpreta como una solicitud a un host llamado doconut, y el Viewer falla con "The viewer handler could not be reached".
Añadir el Viewer a una página
El Viewer es el núcleo requerido de la página. Su superficie de renderizado utiliza dos div anidados:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Trate la barra de herramientas, los montajes de módulos y la superficie del Viewer como una única composición de página. Búsqueda y Anotación inyectan sus cintas incrustadas en montajes opcionales, pero esos módulos nunca son independientes: siempre se adjuntan al Viewer en la misma página. Use el mismo orden que Doconut.TestApp y Doconut.TestApp.Distributed:
<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>Referenciar los recursos del Viewer
En una vista Razor, el servicio Viewer inyectado emite las etiquetas <link> y <script> del viewer en orden de dependencia — el widget es un plugin de jQuery, por lo que jQuery debe cargarse antes de los scripts del viewer:
@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
}))Para el paquete completo del Viewer, solicite los recursos del Viewer y los módulos juntos:
@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 y IncludeViewerScripts son los indicadores centrales obligatorios. Nunca publique un ejemplo de cinta de Búsqueda o Anotación sin ellos, sin el montaje del Viewer y sin una instancia de docViewer. ReferenceCss y ReferenceScripts omiten los recursos de un módulo opcional cuando la licencia actual no otorga esa capacidad; el Viewer central aún se inicia.
Inicializar el visor
El widget del lado del cliente es un plugin de jQuery. Este es un conjunto mínimo de opciones reales de inicialización (no pseudocódigo):
let searchBar = null;
let annBar = null;
const objViewer = $('#div_ctlDoc').docViewer({
showThumbs: true,
autoLoad: false,
pageZoom: 100,
FitType: 'width',
BasePath: 'doconut', // sin '/' inicial
ResPath: 'doconut-res/images', // sin '/' inicial
onViewerReady: function () {
// las páginas son visibles; es seguro ocultar un spinner de carga aquí
},
// Reenviar eventos del ciclo de vida de anotaciones al ribbon incrustado cuando está presente.
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: function (message) {
console.error('Doconut viewer error:', message);
}
});El uso de mayúsculas y minúsculas en las opciones es realmente mixto — showThumbs, autoLoad y pageZoom están en camelCase, pero FitType, BasePath y ResPath están en PascalCase. No hay una regla consistente; si se escribe mal la capitalización, la opción se ignora silenciosamente (el widget vuelve a su valor predeterminado en lugar de lanzar un error).
Ensamblar el paquete completo del Visor
Ambas aplicaciones de referencia .NET 8 instalan las siguientes partes juntas en una página:
| Parte del paquete | Requisito | Cómo se conecta |
|---|---|---|
Recursos del visor, montaje y objViewer | Requerido | Renderizador central de documentos |
| Barra de herramientas del visor | Requerido en la composición de referencia | Marcado del host; los botones llaman al mismo objViewer |
| Cinta de búsqueda | Opcional, módulo con licencia | doconutSearchBar(...).attach(objViewer) |
| Cinta de anotaciones | Opcional, módulo con licencia | doconutAnnotationBar(...).attach(objViewer) |
Aunque la barra de herramientas principal del Visor es marcado del host, se instala junto al Visor y nunca debe documentarse como un control aislado. Esto mantiene su diseño, etiquetas, íconos y reglas de autorización bajo el control de tu aplicación mientras cada botón controla la misma instancia del Visor:
<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 de herramientas de referencia completa también copia wwwroot/js/viewerToolbar.js en la aplicación host para rotación, miniaturas, impresión, pantalla completa, diseño y ayudas de estado de botones. Carga ese archivo host después de Viewer.ReferenceScripts(...). Mantén la ayuda y su marcado <nav id="toolbar"> juntos al copiar la implementación completa de la demostración.
Mantén el orden de inicialización del paquete usado por ambas aplicaciones de referencia:
- Emite los recursos del Visor, Búsqueda y Anotaciones juntos.
- Renderiza la barra de herramientas del Visor, los montajes de Ribbon y el montaje del Visor juntos.
- Inicializa
docViewerprimero. - Crea cada Ribbon con licencia y lo adjunta a ese mismo
objViewer. - Abre el documento y conserva su token para las solicitudes de módulos.
Doconut.TestApp.Distributed mantiene esta composición exacta de UI y la misma ayuda de barra de herramientas del Visor. Su valor adicional de solicitud access y la configuración de reintentos de renderizado asíncrono pertenecen al transporte distribuido; no cambian cómo se ensamblan el Visor, la barra de herramientas o los Ribbons.
Los guardias del lado del servidor son importantes: cuando una capacidad opcional no está disponible, su script no se emite, por lo que su función de plugin jQuery no existe.
<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>Ambos componentes incrustados generan su propio DOM de Ribbon. Búsqueda contiene los grupos Encontrar, Opciones y Resultados. Anotaciones contiene sus herramientas de autoría, controles de estilo, acciones de guardado y acciones opcionales de exportación/imagen. Las barras exponen open(), close(), reset() e isOpen(); siempre llama a attach(objViewer) una vez después de crearlas.
El ejemplo anterior omite callbacks opcionales del host y los puntos finales de exportación/imagen de Anotaciones para mantener el arranque al mínimo. Consulta Búsqueda y Anotaciones para la configuración completa específica de cada característica, o Temas personalizados para estilizar o reemplazar la barra de herramientas del Visor propiedad del host.
Abrir un documento
El lado del servidor tiene un único endpoint: el servicio Viewer inyectado abre el documento y devuelve un token de sesión.
app.MapPost("/api/open", async (Viewer viewer) =>
{
// El token es opaco — páselo al widget, nunca lo registre ni lo persista.
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { token });
});El cliente obtiene ese token y lo pasa al widget con objViewer.View(token):
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});Cerrar el documento
Llame a objViewer.Close() cuando el usuario abandone el visor o abra un documento de reemplazo. En flujos de trabajo dirigidos por el servidor, viewer.CloseDocument(token) elimina inmediatamente la sesión en caché, libera el motor de renderizado, borra su marcador de seguridad y revoca el token. La expiración deslizante eventualmente realiza la misma limpieza, pero se recomienda cerrar explícitamente para documentos grandes.
El flujo de solicitud completado es:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentTrate el token como una credencial portadora: nunca lo registre, nunca lo persista, páselo solo al widget. Identifica una sesión de documento activa en el servidor y deja de funcionar cuando esa sesión expira — vuelva a abrir el documento para obtener uno nuevo.
Ejecutarlo
Coloque un PDF en wwwroot/files/Sample.pdf, ejecute dotnet run y abra la página que aloja el widget. La primera página se renderiza en el visor, con un panel de miniaturas a la izquierda. Si no lo hace, consulte Solución de problemas.
Qué obtienes sin una licencia
Una licencia ausente no genera una excepción. El visor renderiza normalmente, pero cada página lleva una marca de agua de evaluación. Consulte Configuración de licencia para saber cómo Doconut encuentra una licencia y qué cambia una vez que lo hace.
¿Fue útil esta página?