Inicio rápido

Renderiza tu primer documento en minutos

Este tutorial 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 ser la primera. Las llamadas a la sesión también son necesarias — la seguridad de documentos predeterminada de Doconut valida cada solicitud de página contra el estado de sesión de ASP.NET. ¿Ya registraste Doconut durante la Instalación? Salta a la siguiente sección.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddSession(); // Doconut document security rides on ASP.NET session state

app.UseSession();          // call UseSession() before UseDoconut()
app.UseDoconutResources(); // must be registered before UseDoconut()
app.UseDoconut();

Para una disposición de rutas al estilo producción, asigna el middleware de documentos a una rama explícita y mantén alineadas las cuatro configuraciones de ruta:

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = Path.Combine(AppContext.BaseDirectory, "Doconut.Viewer.lic");
    options.MiddlewarePath = "/doconut";
    options.ResourcesPath = "/doconut-res";
    options.UnsafeMode = false;
});
builder.Services.AddSession();

app.UseRouting();
app.UseSession();
app.UseDoconutResources();
app.Map("/doconut", branch => branch.UseDoconut());

MiddlewarePath es un valor de coordinación; no asigna una rama de ASP.NET Core por sí mismo. En este ejemplo el host asigna /doconut, por lo que el cliente debe usar BasePath: '/doconut'. ResourcesPath sirve el paquete incrustado en /doconut-res, y la ruta de recursos de imágenes del widget es, por tanto, ResPath: '/doconut-res/images'.

Añadir el visor a una página

El Viewer es el núcleo requerido de la página. Su superficie de renderizado usa dos div anidados:

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

Trata 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. Usa el mismo orden que Doconut.TestApp y 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>

Referenciar los recursos del visor

En una vista Razor, el servicio Viewer inyectado emite los tags <link> y <script> del visor en orden de dependencia — el widget es un plugin de jQuery, por lo que jQuery debe cargarse antes de los scripts del visor:

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

Para el paquete completo del Viewer, solicita los recursos del Viewer y de los módulos juntos:

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 y IncludeViewerScripts son los indicadores obligatorios del núcleo. Nunca publiques un ejemplo de cinta de Búsqueda o Anotación sin ellos, el montaje del Viewer y una instancia 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):

javascript
let searchBar = null;
let annBar = null;

const objViewer = $('#div_ctlDoc').docViewer({
    showThumbs: true,
    autoLoad:   false,
    pageZoom:   100,
    FitType:    'width',
    BasePath:   '/doconut',
    ResPath:    '/doconut-res/images',
    onViewerReady: function () {
        // pages are visible; safe to hide a loading spinner here
    },
    // Forward annotation lifecycle events to the embedded ribbon when it is present.
    onAnnLoaded:    () => annBar?.handleAnnLoaded(),
    onAnnSaved:     () => annBar?.handleAnnSaved(),
    onAnnSaveError: () => annBar?.handleAnnSaveError(),
    onAnnClosed:    () => annBar?.handleAnnClosed(),
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

La capitalización de las opciones es realmente mixta — showThumbs, autoLoad y pageZoom están en camelCase, pero FitType, BasePath y ResPath están en PascalCase. No hay una regla consistente; si la capitalización es incorrecta la opción se ignora silenciosamente (el widget recurre a su valor predeterminado en lugar de lanzar un error).

Ensamblar el paquete completo del visor

Ambas aplicaciones de referencia .NET 6 instalan las siguientes partes juntas en una página:

Parte del paqueteRequerimientoCómo se conecta
Recursos del visor, montaje y objViewerRequeridoRenderizador de documento central
Barra de herramientas del visorRequerido en la composición de referenciaMarcado del host; los botones llaman al mismo objViewer
Cinta de búsquedaOpcional, módulo con licenciadoconutSearchBar(...).attach(objViewer)
Cinta de anotaciónOpcional, módulo con licenciadoconutAnnotationBar(...).attach(objViewer)

Aunque la barra principal del Viewer es marcado del host, se instala junto al Viewer 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 Viewer:

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 de referencia completa también copia wwwroot/js/viewerToolbar.js al host para rotación, miniaturas, impresión, pantalla completa, disposición y ayudantes de estado de botones. Carga ese archivo del host después de Viewer.ReferenceScripts(...). Mantén el ayudante y su marcado <nav id="toolbar"> juntos al copiar la implementación completa de la demo.

Mantén el orden de inicialización del paquete usado por ambas aplicaciones de referencia:

  1. Emitir CSS para el Viewer y los módulos con licencia.
  2. Renderizar la barra de herramientas del Viewer, los montajes de Búsqueda/Anotación y el montaje del Viewer juntos.
  3. Emitir scripts para el Viewer y los módulos con licencia.
  4. Cargar viewerToolbar.js de la aplicación host.
  5. Inicializar docViewer y conservar el objViewer resultante.
  6. Inicializar cada cinta de Búsqueda o Anotación con licencia.
  7. Llamar attach(objViewer) en cada cinta.
  8. Abrir el documento y retener su token para solicitudes del Viewer y de los módulos.

Doconut.TestApp.Distributed mantiene esta composición UI exacta y el mismo ayudante de barra del Viewer. Su valor de solicitud adicional access y la configuración de reintentos de renderizado asíncrono pertenecen al transporte distribuido; no cambian cómo se ensamblan el Viewer, la barra de herramientas o las cintas.

Los guardias del lado del servidor son importantes: cuando una capacidad opcional no está disponible, su script no se emite, por lo que la función del plugin jQuery no existe.

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>

Ambos componentes incrustados generan su propio DOM de cinta. Búsqueda contiene los grupos Encontrar, Opciones y Resultados. Anotación 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() y 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 Anotación para mantener el arranque mínimo. Consulta Búsqueda, Anotaciones para la configuración completa específica de la característica, o Temas personalizados para estilizar o reemplazar la barra de herramientas del Viewer propiedad del host.

Abrir un documento

El lado del servidor es un único endpoint: el servicio Viewer inyectado abre el documento y devuelve un token de sesión.

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // The token is opaque — hand it to the widget, never log or persist it.
    string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

El cliente recupera ese token y lo pasa al widget con objViewer.View(token):

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

Cerrar el documento

Llama a objViewer.Close() cuando el usuario abandona el visor o abre un documento de reemplazo. En flujos de trabajo impulsados por el servidor, viewer.CloseDocument(token) elimina inmediatamente la sesión en caché, dispone del motor de renderizado, elimina 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:

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

Trata el token como una credencial portadora: nunca lo registres, nunca lo persistas, pásalo solo al widget. Identifica una sesión de documento activa en el servidor y deja de funcionar cuando esa sesión expira — vuelve a abrir el documento para obtener uno nuevo.

Ejecutar

Coloca un PDF en wwwroot/files/Sample.pdf, ejecuta dotnet run y abre 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, consulta Solución de problemas.

Qué obtienes sin una licencia

Una licencia ausente no lanza excepción. El visor se renderiza normalmente, pero cada página lleva una marca de agua de evaluación. Consulta 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?