Mulai 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 peramban: 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() menyambungkan middleware. Panggilan sumber daya harus datang pertama. Panggilan sesi juga diperlukan — keamanan dokumen Doconut 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 Installation? 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 menambahkan satu)
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 bergaya produksi, petakan middleware dokumen ke cabang eksplisit dan jaga keempat 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 secara mandiri. Pada 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: penampil menambahkan satu secara otomatis, sehingga '/doconut' akan menjadi //doconut/DocImage.axd, yang dibaca peramban sebagai permintaan ke host bernama doconut, dan penampil gagal dengan "The viewer handler could not be reached".

Tambahkan penampil ke halaman

Penampil adalah inti yang diperlukan pada halaman. Permukaan render-nya menggunakan dua div bersarang:

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

Perlakukan toolbar, mount modul, dan permukaan Penampil sebagai satu komposisi halaman. Search dan Annotation menyuntikkan pita tersemat mereka ke mount opsional, tetapi modul-modul tersebut tidak pernah berdiri sendiri: mereka selalu menempel pada Penampil 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 Penampil, zoom, tombol Search, dan Annotation -->
</nav>

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

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

Referensikan aset penampil

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

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

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

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

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',             // tanpa '/' di depan
    ResPath:    'doconut-res/images',  // tanpa '/' di depan
    onViewerReady: function () {
        // halaman sudah terlihat; aman untuk menyembunyikan spinner pemuatan di sini
    },
    // Meneruskan 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‑aduk — showThumbs, autoLoad, dan pageZoom memakai camelCase, tetapi FitType, BasePath, dan ResPath memakai PascalCase. Tidak ada aturan konsisten; jika penulisan huruf salah, opsi akan diabaikan secara diam‑diam (widget akan kembali ke nilai bakunya alih‑alih melemparkan kesalahan).

Susun paket Penampil lengkap

Kedua aplikasi referensi .NET 6 memasang bagian‑bagian berikut secara bersamaan pada satu halaman:

Bagian paketPersyaratanCara menghubungkannya
Sumber daya Penampil, mount, dan objViewerWajibRenderer dokumen inti
Bilah alat PenampilWajib dalam komposisi referensiMarkup host; tombol‑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 bersama Penampil dan tidak pernah didokumentasikan sebagai kontrol terpisah. Ini menjaga tata letak, label, ikon, dan aturan otorisasi tetap berada di bawah kendali 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 fungsi rotasi, thumbnail, cetak, layar penuh, tata letak, dan bantuan status tombol. Muat berkas host itu setelah Viewer.ReferenceScripts(...). Simpan helper dan markup <nav id="toolbar">‑nya bersama‑sama saat menyalin implementasi demo penuh.

Pertahankan urutan inisialisasi paket yang dipakai oleh kedua aplikasi referensi:

  1. Emit CSS untuk Penampil dan modul berlisensi.
  2. Render bilah alat Penampil, mount Pencarian/Anotasi, dan mount Penampil secara bersamaan.
  3. Emit skrip untuk Penampil dan modul berlisensi.
  4. Muat viewerToolbar.js milik aplikasi host.
  5. Inisialisasi docViewer dan simpan objViewer yang dihasilkan.
  6. Inisialisasi setiap Pita Pencarian atau Anotasi yang berlisensi.
  7. Panggil attach(objViewer) pada setiap Pita.
  8. Buka dokumen dan simpan tokennya untuk permintaan Penampil dan modul.

Doconut.TestApp.Distributed mempertahankan komposisi UI persis ini serta helper bilah alat Penampil yang sama. Nilai permintaan access tambahan serta pengaturan ulang render asinkron milik transport terdistribusi; keduanya tidak mengubah cara Penampil, bilah alat, atau Pita dirakit.

Penjaga sisi server penting: ketika kemampuan opsional tidak tersedia, skripnya tidak di‑emit, sehingga fungsi plugin jQuery 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. Pita Pencarian berisi grup Find, Options, dan Results. Pita Anotasi berisi alat penulisan, kontrol gaya, aksi simpan, serta aksi ekspor/gambar opsional. Pita‑pita tersebut mengekspos open(), close(), reset(), dan isOpen(); selalu panggil attach(objViewer) sekali setelah membuatnya.

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

Buka dokumen

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

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // Token bersifat opak — 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);
    });

Tutup dokumen

Panggil objViewer.Close() ketika pengguna meninggalkan viewer atau membuka dokumen pengganti. Pada alur kerja yang dipandu server, viewer.CloseDocument(token) langsung menghapus sesi yang di‑cache, membuang mesin rendering, menghapus penanda keamanannya, dan mencabut token. Kedaluwarsa bergulir pada akhirnya melakukan pembersihan yang sama, tetapi 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 log, jangan pernah simpan, berikan hanya ke widget. Token ini mengidentifikasi sesi dokumen aktif di server dan tidak berfungsi lagi ketika sesi tersebut kedaluwarsa — buka kembali dokumen untuk mendapatkan token baru.

Jalankan

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

Apa yang Anda dapatkan tanpa lisensi

Lisensi yang hilang tidak menyebabkan error. Viewer tetap 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?