Memulai 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 Penampil lengkap (toolbar Penampil, mount Penampil, dan pita Pencarian/Anotasi opsional), referensi aset, inisialisasi klien, pembukaan dokumen, dan eksekusi.
Pengaturan Server
AddDoconut() mendaftarkan layanan; UseDoconutResources() dan UseDoconut() menyambungkan middleware. Panggilan sumber daya harus ditempatkan pertama. Panggilan sesi juga diperlukan — keamanan dokumen Doconut memvalidasi setiap permintaan halaman terhadap status sesi ASP.NET. Sudah mendaftarkan Doconut selama Instalasi? Lewati ke bagian berikutnya.
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();Untuk tata letak jalur gaya produksi, petakan middleware dokumen ke cabang eksplisit dan pertahankan empat pengaturan jalur tetap selaras:
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 adalah nilai koordinasi; ia tidak memetakan cabang ASP.NET Core secara otomatis. Dalam contoh ini host memetakan /doconut, sehingga klien harus menggunakan BasePath: '/doconut'. ResourcesPath menyajikan bundel tersemat di /doconut-res, dan jalur sumber daya gambar widget menjadi ResPath: '/doconut-res/images'.
Tambahkan penampil ke halaman
Penampil adalah inti yang diperlukan pada halaman. Permukaan render‑nya menggunakan dua div bersarang:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Anggap toolbar, mount modul, dan permukaan Penampil sebagai satu komposisi halaman. Pencarian dan Anotasi 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:
<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>Referensikan aset penampil
Di 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:
@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.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 Pita Pencarian atau Anotasi 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. Berikut adalah sekumpulan opsi inisialisasi nyata (bukan pseudocode):
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);
}
});Penulisan huruf pada opsi memang campur‑aduk — showThumbs, autoLoad, dan pageZoom memakai camelCase, sementara 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 baku alih‑alih melempar error).
Rakit paket Penampil lengkap
Kedua aplikasi referensi .NET 8 menginstal bagian‑bagian berikut secara bersamaan pada satu halaman:
| Bagian dari paket | Kebutuhan | Cara terhubung |
|---|---|---|
Viewer resources, mount, and objViewer | Diperlukan | Core document renderer |
| Viewer toolbar | Diperlukan dalam komposisi referensi | Markup host; tombol memanggil objViewer yang sama |
| Search ribbon | Opsional, modul berlisensi | doconutSearchBar(...).attach(objViewer) |
| Annotation ribbon | Opsional, modul berlisensi | doconutAnnotationBar(...).attach(objViewer) |
Meskipun toolbar 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 di bawah kendali aplikasi Anda sementara setiap tombol mengendalikan instance Penampil yang sama:
<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>Toolbar 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 berkas host itu setelah Viewer.ReferenceScripts(...). Simpan pembantu dan markup <nav id="toolbar">‑nya bersama saat menyalin implementasi demo penuh.
Pertahankan urutan inisialisasi paket yang digunakan oleh kedua aplikasi referensi:
- Emit sumber daya Penampil, Pencarian, dan Anotasi secara bersamaan.
- Render toolbar Penampil, mount Pita, dan mount Penampil secara bersamaan.
- Inisialisasi
docViewerterlebih dahulu. - Buat setiap Pita berlisensi dan lampirkan ke
objVieweryang sama. - Buka dokumen dan simpan tokennya untuk permintaan modul.
Doconut.TestApp.Distributed mempertahankan komposisi UI persis ini serta pembantu toolbar Penampil yang sama. Nilai permintaan access tambahan dan pengaturan ulang‑coba render asinkron milik transportasi terdistribusi; keduanya tidak mengubah cara Penampil, toolbar, atau Pita dirakit.
Pengaman sisi server penting: ketika kemampuan opsional tidak tersedia, skripnya tidak di‑emit, sehingga fungsi plugin jQuery tidak ada.
<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 Find, Options, dan Results. Anotasi berisi alat authoring, kontrol gaya, aksi simpan, serta aksi ekspor/gambar opsional. Bar‑bar 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 toolbar Penampil milik host.
Buka dokumen
Sisi server hanya satu endpoint: layanan Viewer yang disuntikkan membuka dokumen dan mengembalikan token sesi.
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 });
});Klien mengambil token itu dan memberikannya ke widget dengan objViewer.View(token):
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 penampil atau membuka dokumen pengganti. Pada alur kerja yang digerakkan server, viewer.CloseDocument(token) segera menghapus sesi cache, membuang mesin render, menghapus penanda keamanan, dan mencabut token. Kedaluwarsa bergulir pada akhirnya melakukan pembersihan yang sama, tetapi penutupan eksplisit disarankan untuk dokumen berukuran besar.
Alur permintaan yang selesai adalah:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentPerlakukan token seperti kredensial bearer: jangan pernah mencatatnya, jangan pernah menyimpannya, serahkan hanya ke widget. Token mengidentifikasi sesi dokumen aktif di server dan tidak akan 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 akan dirender di penampil, dengan panel thumbnail di sebelah kiri. Jika tidak muncul, lihat Pemecahan Masalah.
Apa yang Anda dapatkan tanpa lisensi
Lisensi yang hilang tidak menyebabkan error. Penampil tetap merender normal, tetapi setiap halaman menampilkan watermark evaluasi. Lihat Pengaturan Lisensi untuk cara Doconut menemukan lisensi dan apa yang berubah setelah ditemukan.
Apakah halaman ini membantu?