Panduan Cepat

Render dokumen pertama Anda dalam hitungan menit

Panduan ini membawa aplikasi ASP.NET Core dari Program.cs yang kosong hingga dokumen yang dirender di browser: pendaftaran server, paket Viewer lengkap (toolbar Viewer, mount Viewer, dan pita Search/Annotation opsional), referensi aset, inisialisasi klien, pembukaan dokumen, dan eksekusi.

Pengaturan Server

AddDoconut() mendaftarkan layanan; UseDoconutResources() dan UseDoconut() menghubungkan middleware. Panggilan resources harus dilakukan pertama. Panggilan sesi juga diperlukan — keamanan dokumen Doconut secara default memvalidasi setiap permintaan halaman terhadap status sesi ASP.NET. Status sesi membutuhkan cache: AddDistributedMemoryCache() menyediakan satu. Aplikasi MVC atau Razor Pages sudah mendaftarkannya, tetapi aplikasi minimal-API tidak, dan tanpa itu aplikasi berhenti saat startup dengan Unable to resolve service for type 'IDistributedCache'. Sudah mendaftarkan Doconut selama Instalasi? Lewati ke bagian berikutnya.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // status sesi membutuhkan cache (MVC/Razor Pages sudah menambahkannya)
builder.Services.AddSession(); // Keamanan dokumen Doconut bergantung pada status sesi ASP.NET

app.UseSession();          // panggil UseSession() sebelum UseDoconut()
app.UseDoconutResources(); // harus didaftarkan sebelum UseDoconut()
app.UseDoconut();

Untuk tata letak jalur gaya produksi, petakan middleware dokumen ke cabang eksplisit dan pertahankan empat pengaturan jalur tetap selaras:

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 adalah nilai koordinasi; ia tidak memetakan cabang ASP.NET Core sendiri. Dalam contoh ini host memetakan /doconut, sehingga klien menggunakan BasePath: 'doconut'. ResourcesPath menyajikan bundel tersemat di /doconut-res, dan jalur sumber daya gambar widget oleh karena itu ResPath: 'doconut-res/images'. Tulis keduanya tanpa garis miring di depan: viewer menambahkan satu secara otomatis, sehingga '/doconut' akan menjadi //doconut/DocImage.axd, yang dibaca browser sebagai permintaan ke host bernama doconut, dan viewer gagal dengan "The viewer handler could not be reached".

Tambahkan viewer ke halaman

Viewer adalah inti yang diperlukan dari halaman. Permukaan rendering-nya menggunakan dua div bersarang:

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

Anggap toolbar, mount modul, dan permukaan Viewer sebagai satu komposisi halaman. Search dan Annotation menyuntikkan pita tersemat mereka ke mount opsional, tetapi modul-modul tersebut tidak pernah berdiri sendiri: mereka selalu terpasang pada Viewer di halaman yang sama. Gunakan urutan yang sama seperti Doconut.TestApp dan Doconut.TestApp.Distributed:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- Navigasi Viewer, zoom, pencarian, dan tombol Anotasi -->
</nav>

<div id="searchBarMount"></div>
<div id="annBarMount"></div>

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

Referensikan aset viewer

Dalam tampilan Razor, layanan Viewer yang disuntik menghasilkan tag <link> dan <script> viewer dalam urutan ketergantungan — widget adalah plugin jQuery, sehingga jQuery harus dimuat sebelum skrip viewer:

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

Untuk paket Viewer lengkap, minta sumber daya Viewer dan modul secara bersamaan:

csharp
@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 dan IncludeViewerScripts adalah flag inti yang wajib. Jangan pernah memublikasikan contoh Ribbon Search atau Annotation tanpa mereka, mount Viewer, dan sebuah instance docViewer. ReferenceCss dan ReferenceScripts menghilangkan sumber daya modul opsional ketika lisensi saat ini tidak memberikan kemampuan tersebut; Viewer inti tetap berjalan.

Inisialisasi Penampil

Widget sisi klien adalah plugin jQuery. Ini adalah sekumpulan opsi inisialisasi nyata yang minimal (bukan pseudocode):

javascript
let searchBar = null;
let annBar = null;

const objViewer = $('#div_ctlDoc').docViewer({
    showThumbs: true,
    autoLoad:   false,
    pageZoom:   100,
    FitType:    'width',
    BasePath:   'doconut',             // tidak ada '/' di depan
    ResPath:    'doconut-res/images',  // tidak ada '/' di depan
    onViewerReady: function () {
        // halaman terlihat; aman untuk menyembunyikan pemuat di sini
    },
    // Teruskan peristiwa siklus hidup anotasi ke pita tersemat ketika ada.
    onAnnLoaded:    () => annBar?.handleAnnLoaded(),
    onAnnSaved:     () => annBar?.handleAnnSaved(),
    onAnnSaveError: () => annBar?.handleAnnSaveError(),
    onAnnClosed:    () => annBar?.handleAnnClosed(),
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

Penulisan huruf pada opsi memang campur — showThumbs, autoLoad, dan pageZoom menggunakan camelCase, tetapi FitType, BasePath, dan ResPath menggunakan PascalCase. Tidak ada aturan konsisten; jika penulisan huruf salah, opsi akan diabaikan secara diam-diam (widget akan kembali ke nilai default alih-alih melempar kesalahan).

Susun paket Penampil lengkap

Kedua aplikasi referensi .NET 8 memasang bagian-bagian berikut bersama-sama pada satu halaman:

Bagian paketPersyaratanCara terhubung
Sumber daya Penampil, mount, dan objViewerDiperlukanRenderer dokumen inti
Bilah alat PenampilDiperlukan dalam komposisi referensiMarkup host; tombol memanggil objViewer yang sama
Pita pencarianOpsional, modul berlisensidoconutSearchBar(...).attach(objViewer)
Pita anotasiOpsional, modul berlisensidoconutAnnotationBar(...).attach(objViewer)

Meskipun bilah alat Penampil utama adalah markup host, ia dipasang bersamaan dengan Penampil dan tidak boleh pernah didokumentasikan sebagai kontrol terpisah. Ini menjaga tata letak, label, ikon, dan aturan otorisasi di bawah kontrol aplikasi Anda sementara setiap tombol mengendalikan instance Penampil yang sama:

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>

Bilah alat referensi lengkap juga menyalin wwwroot/js/viewerToolbar.js ke dalam aplikasi host untuk rotasi, thumbnail, cetak, layar penuh, tata letak, dan pembantu status tombol. Muat file host itu setelah Viewer.ReferenceScripts(...). Simpan pembantu dan markup <nav id="toolbar">-nya bersama-sama saat menyalin implementasi demo lengkap.

Pertahankan urutan inisialisasi paket yang digunakan oleh kedua aplikasi referensi:

  1. Emit sumber daya Penampil, Pencarian, dan Anotasi secara bersamaan.
  2. Render bilah alat Penampil, pemasangan Pita, dan pemasangan Penampil secara bersamaan.
  3. Inisialisasi docViewer terlebih dahulu.
  4. Buat setiap Pita berlisensi dan lampirkan ke objViewer yang sama.
  5. Buka dokumen dan simpan tokennya untuk permintaan modul.

Doconut.TestApp.Distributed mempertahankan komposisi UI persis ini dan pembantu bilah alat Penampil yang sama. Nilai permintaan access tambahan dan pengaturan ulang coba render-asinkron milik transportasi terdistribusi; mereka tidak mengubah cara Penampil, bilah alat, atau Pita dirakit.

Penjaga sisi server penting: ketika kemampuan opsional tidak tersedia, skripnya tidak diemit, sehingga fungsi plug-in jQuery-nya tidak ada.

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>

Kedua komponen tersemat menghasilkan DOM Pita masing-masing. Pencarian berisi grup Temukan, Opsi, dan Hasil. Anotasi berisi alat penulisan, kontrol gaya, aksi simpan, dan aksi ekspor/gambar opsional. Pita-pita tersebut menyediakan open(), close(), reset(), dan isOpen(); selalu panggil attach(objViewer) sekali setelah membuatnya.

Contoh di atas menghilangkan panggilan balik host opsional dan endpoint ekspor/gambar Anotasi untuk menjaga startup tetap minimal. Lihat Pencarian dan Anotasi untuk pengaturan fitur lengkap, atau Tema Kustom untuk menata atau mengganti bilah alat Penampil milik host.

Membuka dokumen

Sisi server memiliki satu endpoint: layanan Viewer yang disuntik membuka dokumen dan mengembalikan token sesi.

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // Token bersifat tidak transparan — berikan ke widget, jangan pernah log atau simpan.
    string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

Klien mengambil token tersebut dan memberikannya ke widget dengan objViewer.View(token):

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

Panggil objViewer.Close() ketika pengguna meninggalkan viewer atau membuka dokumen pengganti. Pada alur kerja yang dikendalikan server, viewer.CloseDocument(token) segera menghapus sesi yang di-cache, membuang mesin rendering, menghapus penanda keamanannya, dan mencabut token. Kedaluwarsa bergulir pada akhirnya melakukan pembersihan yang sama, namun penutupan eksplisit disarankan untuk dokumen berukuran besar.

Alur permintaan yang selesai adalah:

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

Perlakukan token seperti kredensial bearer: jangan pernah mencatatnya, jangan pernah menyimpannya, berikan hanya kepada widget. Token ini mengidentifikasi sesi dokumen yang aktif di server dan tidak berfungsi lagi ketika sesi tersebut kedaluwarsa — buka kembali dokumen untuk mendapatkan token baru.

Menjalankannya

Letakkan file PDF di wwwroot/files/Sample.pdf, jalankan dotnet run, dan buka halaman yang menampung widget. Halaman pertama akan dirender di viewer, dengan panel thumbnail di sebelah kiri. Jika tidak, lihat Pemecahan Masalah.

Apa yang Anda dapatkan tanpa lisensi

Lisensi yang hilang tidak menyebabkan error. Viewer merender secara normal, tetapi setiap halaman menampilkan watermark evaluasi. Lihat Pengaturan Lisensi untuk cara Doconut menemukan lisensi dan apa yang berubah setelahnya.

Apakah halaman ini membantu?