เริ่มต้นอย่างเร็ว
เรนเดอร์เอกสารแรกของคุณในไม่กี่นาที
บทนำนี้จะพาแอป ASP.NET Core จากไฟล์ Program.cs ว่างเปล่า ไปสู่การแสดงเอกสารในเบราว์เซอร์: การลงทะเบียนเซิร์ฟเวอร์, แพ็กเกจ Viewer ครบชุด (แถบเครื่องมือ Viewer, การเมานท์ Viewer, และริบบอนค้นหา/บันทึกหมายเหตุแบบเลือก), การอ้างอิงทรัพยากร, การเริ่มต้นไคลเอนต์, การเปิดเอกสาร, และการดำเนินการ.
การตั้งค่าเซิร์ฟเวอร์
AddDoconut() ลงทะเบียนบริการ; UseDoconutResources() และ UseDoconut() เชื่อมต่อมิดเดิลแวร์. การเรียกใช้ resources ต้องมาก่อน. การเรียกใช้ session ก็จำเป็นเช่นกัน — ความปลอดภัยเอกสารเริ่มต้นของ Doconut ตรวจสอบคำขอแต่ละหน้าเทียบกับสถานะเซสชันของ ASP.NET. สถานะเซสชันต้องการแคช: AddDistributedMemoryCache() จะให้หนึ่งอัน. แอป MVC หรือ Razor Pages จะลงทะเบียนไว้แล้ว, แต่แอป minimal‑API ไม่ได้, และหากไม่มีแคช แอปจะหยุดทำงานที่การเริ่มต้นด้วยข้อความ Unable to resolve service for type 'IDistributedCache'. ได้ลงทะเบียน Doconut ไว้แล้วในขั้นตอน การติดตั้ง? ข้ามไปยังส่วนต่อไป.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // สถานะเซสชันต้องการแคช (MVC/Razor Pages มีการเพิ่มไว้แล้ว)
builder.Services.AddSession(); // ความปลอดภัยเอกสาร Doconut ทำงานบนสถานะเซสชันของ ASP.NET
app.UseSession(); // เรียก UseSession() ก่อน UseDoconut()
app.UseDoconutResources(); // ต้องลงทะเบียนก่อน UseDoconut()
app.UseDoconut();สำหรับการจัดวางเส้นทางแบบการผลิต, ให้แมปมิดเดิลแวร์เอกสารไปยังสาขาเฉพาะและรักษาการตั้งค่าเส้นทางสี่ค่าให้สอดคล้องกัน:
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 เป็นค่าประสานงาน; มันไม่ได้แมปสาขา ASP.NET Core ด้วยตนเอง. ในตัวอย่างนี้โฮสต์แมป /doconut, ดังนั้นไคลเอนต์จะใช้ BasePath: 'doconut'. ResourcesPath ให้บริการบันเดิลฝังที่ /doconut-res, และเส้นทางทรัพยากรรูปภาพของวิดเจ็ตจึงเป็น ResPath: 'doconut-res/images'. เขียนทั้งสอง โดยไม่มีสแลชนำหน้า: viewer จะเพิ่มสแลชให้เอง, ดังนั้น '/doconut' จะกลายเป็น //doconut/DocImage.axd, ซึ่งเบราว์เซอร์จะตีความเป็นการร้องขอไปยังโฮสต์ชื่อ doconut, และ viewer จะล้มเหลวด้วยข้อความ "The viewer handler could not be reached".
เพิ่ม Viewer ลงในหน้า
Viewer เป็นแกนหลักที่จำเป็นของหน้า. พื้นผิวการเรนเดอร์ของมันใช้ div สองชั้นซ้อนกัน:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>ให้ถือแถบเครื่องมือ, การเมานท์โมดูล, และพื้นผิว Viewer เป็นการจัดองค์ประกอบหน้าเดียวกัน. การค้นหาและบันทึกหมายเหตุจะฉีดริบบอนฝังของพวกเขาเข้าไปในการเมานท์แบบเลือก, แต่โมดูลเหล่านั้นไม่มีการทำงานแยกจากกัน: พวกมันจะผูกติดกับ Viewer บนหน้าเดียวกันเสมอ. ใช้ลำดับเดียวกับ Doconut.TestApp และ Doconut.TestApp.Distributed:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- การนำทางของ Viewer, การซูม, ปุ่มค้นหาและบันทึกหมายเหตุ -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>อ้างอิงทรัพยากร Viewer
ใน Razor view, เซอร์วิส Viewer ที่ฉีดเข้ามาจะสร้างแท็ก <link> และ <script> ของ viewer ตามลำดับการพึ่งพา — วิดเจ็ตเป็นปลั๊กอิน jQuery, ดังนั้น jQuery ต้องโหลดก่อนสคริปต์ของ 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
}))สำหรับแพ็กเกจ Viewer ครบชุด, ให้ร้องขอทรัพยากร Viewer และโมดูลพร้อมกัน:
@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 และ IncludeViewerScripts เป็นแฟล็กหลักที่จำเป็น. อย่าเผยแพร่ตัวอย่างริบบอน Search หรือ Annotation โดยไม่มีพวกมัน, การเมานท์ Viewer, และอินสแตนซ์ docViewer. ReferenceCss และ ReferenceScripts จะละเว้นทรัพยากรของโมดูลเลือกเมื่อใบอนุญาตปัจจุบันไม่ได้ให้สิทธิ์นั้น; Viewer หลักยังคงเริ่มทำงาน.
เริ่มต้นตัวดูเอกสาร
วิดเจ็ตด้านคลายเอนต์เป็นปลั๊กอิน jQuery นี่คือชุดตัวเลือกการเริ่มต้นที่แท้จริงอย่างน้อยที่สุด (ไม่ใช่โค้ดจำลอง):
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 () {
// หน้าแสดงผลแล้ว; ปลอดภัยที่จะซ่อนสปินเนอร์โหลดที่นี่
},
// ส่งต่อเหตุการณ์วงจรชีวิตของการอธิบายไปยังริบบิ้นที่ฝังอยู่เมื่อมีอยู่.
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: function (message) {
console.error('Doconut viewer error:', message);
}
});รูปแบบการใช้ตัวพิมพ์ใหญ่ของตัวเลือกนั้นผสมกันจริง ๆ — showThumbs, autoLoad และ pageZoom ใช้ camelCase, แต่ FitType, BasePath และ ResPath ใช้ PascalCase. ไม่มีกฎที่สอดคล้อง; หากใช้รูปแบบผิด ตัวเลือกจะถูกละเลยโดยเงียบ (วิดเจ็ตจะกลับไปใช้ค่าเริ่มต้นแทนการแจ้งข้อผิดพลาด).
รวมแพคเกจ Viewer อย่างสมบูรณ์
ทั้งสองแอปพลิเคชันอ้างอิง .NET 8 จะติดตั้งส่วนต่อไปนี้พร้อมกันในหนึ่งหน้า:
| ส่วนของแพคเกจ | ความต้องการ | วิธีการเชื่อมต่อ |
|---|---|---|
ทรัพยากร Viewer, การเมานท์, และ objViewer | จำเป็น | Core document renderer |
| แถบเครื่องมือ Viewer | จำเป็นในโครงสร้างอ้างอิง | Host markup; buttons call the same objViewer |
| ริบบิ้นค้นหา | ไม่บังคับ, โมดูลที่มีลิขสิทธิ์ | doconutSearchBar(...).attach(objViewer) |
| ริบบิ้นการอธิบาย | ไม่บังคับ, โมดูลที่มีลิขสิทธิ์ | doconutAnnotationBar(...).attach(objViewer) |
แม้ว่าแถบเครื่องมือ Viewer หลักจะเป็นมาร์กอัปโฮสต์, มันจะถูกติดตั้งพร้อมกับ Viewer และไม่ควรจะถูกบันทึกเป็นคอนโทรลแยกออก. สิ่งนี้ทำให้การจัดวาง, ป้ายกำกับ, ไอคอน, และกฎการอนุญาตอยู่ภายใต้การควบคุมของแอปพลิเคชันของคุณในขณะที่ทุกปุ่มทำงานกับอินสแตนซ์ Viewer เดียวกัน:
<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>แถบเครื่องมืออ้างอิงเต็มจะคัดลอก wwwroot/js/viewerToolbar.js ไปยังแอปพลิเคชันโฮสต์เพื่อใช้ฟังก์ชันการหมุน, รูปย่อ, พิมพ์, เต็มจอ, การจัดวาง, และตัวช่วยสถานะปุ่ม. โหลดไฟล์โฮสต์นั้นหลังจาก Viewer.ReferenceScripts(...). เก็บตัวช่วยและมาร์กอัป <nav id="toolbar"> ไว้ด้วยกันเมื่อคัดลอกการสาธิตเต็ม.
รักษาลำดับการเริ่มต้นแพคเกจที่ใช้โดยทั้งสองแอปพลิเคชันอ้างอิง:
- ส่งออกทรัพยากร Viewer, Search, และ Annotation พร้อมกัน.
- เรนเดอร์แถบเครื่องมือ Viewer, การเมานท์ริบบิ้น, และการเมานท์ Viewer พร้อมกัน.
- เริ่มต้น
docViewerก่อน. - สร้างริบบิ้นที่มีลิขสิทธิ์แต่ละอันและแนบเข้ากับ
objViewerเดียวกันนั้น. - เปิดเอกสารและเก็บโทเคนของมันสำหรับการร้องขอโมดูล.
Doconut.TestApp.Distributed รักษาการจัดวาง UI นี้อย่างแม่นยำและใช้ตัวช่วยแถบเครื่องมือ Viewer เดียวกัน. ค่า access ที่เพิ่มเข้ามาและการตั้งค่าการลองใหม่แบบเรนเดอร์แบบอะซิงโครนัสเป็นของการส่งผ่านแบบกระจาย; พวกมันไม่เปลี่ยนวิธีการประกอบ Viewer, แถบเครื่องมือ, หรือริบบิ้น.
การป้องกันด้านเซิร์ฟเวอร์มีความสำคัญ: เมื่อความสามารถที่เป็นตัวเลือกไม่พร้อมใช้งาน, สคริปต์ของมันจะไม่ถูกส่งออก, ดังนั้นฟังก์ชันปลั๊กอิน jQuery ของมันจะไม่มีอยู่.
<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>ทั้งสองคอมโพเนนต์ที่ฝังอยู่สร้าง DOM ของริบบิ้นของตนเอง. ค้นหามีกลุ่ม Find, Options, และ Results. การอธิบายมีเครื่องมือสร้าง, ควบคุมสไตล์, การบันทึก, และการกระทำส่งออก/รูปภาพที่เป็นตัวเลือก. ริบบิ้นเหล่านี้เปิดเผยเมธอด open(), close(), reset(), และ isOpen(); ควรเรียก attach(objViewer) หนึ่งครั้งหลังจากสร้างมัน.
ตัวอย่างข้างต้นละเว้นการเรียกกลับของโฮสต์ที่เป็นตัวเลือกและจุดสิ้นสุดการส่งออก/รูปภาพของ Annotation เพื่อให้การเริ่มต้นเป็นอย่างน้อยที่สุด. ดูที่ ค้นหา และ การอธิบาย สำหรับการตั้งค่าฟีเจอร์ที่สมบูรณ์, หรือ ธีมที่กำหนดเอง เพื่อจัดรูปแบบหรือแทนที่แถบเครื่องมือ Viewer ที่เป็นของโฮสต์.
เปิดเอกสาร
ด้านเซิร์ฟเวอร์มีเพียงหนึ่งจุดเชื่อมต่อ: บริการ Viewer ที่ฉีดเข้ามาจะเปิดเอกสารและคืนค่า token ของเซสชัน
app.MapPost("/api/open", async (Viewer viewer) =>
{
// Token นี้เป็นแบบไม่เปิดเผย — ส่งให้วิดเจ็ต, อย่าเก็บบันทึกหรือบันทึกลงฐานข้อมูล
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { token });
});ไคลเอนต์จะดึง token นั้นและส่งให้วิดเจ็ตด้วย objViewer.View(token):
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});ปิดเอกสาร
เรียก objViewer.Close() เมื่อผู้ใช้ออกจากวิดเจ็ตหรือเปิดเอกสารทดแทน ในกระบวนการทำงานที่ควบคุมโดยเซิร์ฟเวอร์, viewer.CloseDocument(token) จะลบเซสชันที่แคชไว้ทันที, ปิดการทำงานของเอนจินการเรนเดอร์, ลบเครื่องหมายความปลอดภัย, และเพิกถอน token การหมดอายุแบบเลื่อนจะทำความสะอาดเช่นเดียวกันในภายหลัง, แต่การปิดอย่างชัดเจนแนะนำสำหรับเอกสารขนาดใหญ่
ขั้นตอนการทำงานของคำขอที่สมบูรณ์คือ:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentปฏิบัติต่อ token เหมือนกับข้อมูลประจำตัวแบบ bearer: อย่าเก็บบันทึก, อย่าเก็บถาวร, ส่งให้วิดเจ็ตเท่านั้น มันระบุเซสชันเอกสารที่กำลังทำงานบนเซิร์ฟเวอร์และจะหยุดทำงานเมื่อเซสชันนั้นหมดอายุ — เปิดเอกสารใหม่เพื่อรับ token ใหม่
รันโปรเจกต์
วางไฟล์ PDF ที่ wwwroot/files/Sample.pdf, รัน dotnet run, แล้วเปิดหน้าที่โฮสต์วิดเจ็ต หน้าแรกจะเรนเดอร์ในวิดเจ็ตพร้อมกับแผงรูปย่อทางด้านซ้าย หากไม่แสดง, ดูที่ การแก้ไขปัญหา
สิ่งที่คุณจะได้โดยไม่มีใบอนุญาต
ใบอนุญาตที่หายไปจะไม่ทำให้เกิดข้อผิดพลาด วิดเจ็ตจะเรนเดอร์ตามปกติ แต่ทุกหน้าจะมีลายน้ำการประเมินผล ดูที่ การตั้งค่าใบอนุญาต เพื่อดูว่า Doconut ค้นหาใบอนุญาตอย่างไรและจะมีการเปลี่ยนแปลงอะไรบ้างเมื่อพบแล้ว
หน้านี้เป็นประโยชน์หรือไม่?