เริ่มต้นอย่างรวดเร็ว

เรนเดอร์เอกสารแรกของคุณในไม่กี่นาที

บทแนะนำนี้นำแอป ASP.NET Core จากไฟล์ Program.cs ว่างเปล่าไปสู่การแสดงเอกสารในเบราว์เซอร์: การลงทะเบียนเซิร์ฟเวอร์, แพ็กเกจ Viewer ครบชุด (แถบเครื่องมือ Viewer, การเมานท์ Viewer, และริบบิ้น Search/Annotation ที่เป็นตัวเลือก), การอ้างอิงทรัพยากร, การเริ่มต้นไคลเอนต์, การเปิดเอกสาร, และการดำเนินการ.

การตั้งค่าเซิร์ฟเวอร์

AddDoconut() ลงทะเบียนบริการ; UseDoconutResources() และ UseDoconut() เชื่อมต่อ middleware. การเรียก resources ต้องมาก่อน. การเรียก session ก็จำเป็นเช่นกัน — ระบบความปลอดภัยเอกสารเริ่มต้นของ Doconut ตรวจสอบคำขอแต่ละหน้าต่อสถานะ session ของ ASP.NET. ได้ลงทะเบียน Doconut ไว้แล้วหรือยังในระหว่าง การติดตั้ง? ข้ามไปยังส่วนถัดไป.

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();

สำหรับการจัดวางเส้นทางแบบการผลิต, ให้แมป middleware ของเอกสารไปยังสาขาที่ระบุอย่างชัดเจนและรักษาการตั้งค่าเส้นทางสี่ค่าให้สอดคล้องกัน:

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 เป็นค่าการประสาน; มันไม่ได้แมปสาขา ASP.NET Core ด้วยตัวเอง. ในตัวอย่างนี้โฮสต์แมป /doconut, ดังนั้นไคลเอนต์ต้องใช้ BasePath: '/doconut'. ResourcesPath ให้บริการบันเดิลที่ฝังอยู่ที่ /doconut-res, และเส้นทางทรัพยากรรูปภาพของวิดเจ็ตจึงเป็น ResPath: '/doconut-res/images'.

เพิ่มตัวดูเอกสารลงในหน้า

Viewer เป็นแกนหลักที่จำเป็นของหน้า. พื้นผิวการเรนเดอร์ของมันใช้ div สองชั้นซ้อนกัน:

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

พิจารณาแถบเครื่องมือ, การเมานท์โมดูล, และพื้นผิว Viewer เป็นการจัดองค์ประกอบหน้าเดียว. Search และ Annotation แทรกริบบิ้นที่ฝังอยู่ลงในการเมานท์ที่เป็นตัวเลือก, แต่โมดูลเหล่านั้นไม่มีการทำงานแบบอิสระ: พวกมันจะผูกกับ Viewer บนหน้าเดียวกันเสมอ. ใช้ลำดับเดียวกับ Doconut.TestApp และ 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>

อ้างอิงทรัพยากรตัวดูเอกสาร

ใน Razor view, เซอร์วิส Viewer ที่ฉีดเข้ามาจะสร้างแท็ก <link> และ <script> ของตัวดูเอกสารตามลำดับการพึ่งพา — วิดเจ็ตเป็นปลั๊กอิน jQuery, ดังนั้นต้องโหลด jQuery ก่อนสคริปต์ของ Viewer:

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

สำหรับแพคเกจ Viewer ครบชุด, ให้ร้องขอทรัพยากร Viewer และโมดูลพร้อมกัน:

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 และ IncludeViewerScripts เป็นแฟล็กหลักที่จำเป็น. อย่าเผยแพร่ตัวอย่างริบบิ้น Search หรือ Annotation โดยไม่มีพวกมัน, การเมานท์ Viewer, และอินสแตนซ์ docViewer. ReferenceCss และ ReferenceScripts จะละเว้นทรัพยากรของโมดูลที่เป็นตัวเลือกเมื่อใบอนุญาตปัจจุบันไม่ให้สิทธิ์; Viewer หลักยังคงเริ่มทำงาน.

เริ่มต้นตัวดูเอกสาร

วิดเจ็ตฝั่งไคลเอนต์เป็นปลั๊กอิน jQuery. นี่คือชุดตัวเลือกการเริ่มต้นที่เป็นจริงและขั้นต่ำ (ไม่ใช่โค้ดเทียม):

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

รูปแบบการเขียนตัวอักษรของตัวเลือกผสมกันจริง ๆ — showThumbs, autoLoad, และ pageZoom ใช้ camelCase, แต่ FitType, BasePath, และ ResPath ใช้ PascalCase. ไม่มีกฎที่สอดคล้อง; หากเขียนรูปแบบผิด ตัวเลือกจะถูกละเลยโดยไม่มีการแจ้ง (วิดเจ็ตจะกลับไปใช้ค่าเริ่มต้นแทนการโยนข้อผิดพลาด).

รวมแพคเกจ Viewer ที่สมบูรณ์

ทั้งสองแอปอ้างอิงของ .NET 6 จะติดตั้งส่วนต่อไปนี้พร้อมกันในหน้าเดียว:

ส่วนของแพคเกจความต้องการวิธีการเชื่อมต่อ
ทรัพยากร Viewer, การเมานท์, และ objViewerจำเป็นเรนเดอร์เอกสารหลัก
แถบเครื่องมือ Viewerจำเป็นในโครงสร้างอ้างอิงมาร์กอัปโฮสต์; ปุ่มเรียก objViewer เดียวกัน
ริบบิ้นการค้นหาโมดูลที่มีใบอนุญาต, ไม่บังคับdoconutSearchBar(...).attach(objViewer)
ริบบิ้นคำอธิบายโมดูลที่มีใบอนุญาต, ไม่บังคับdoconutAnnotationBar(...).attach(objViewer)

แม้ว่าแถบเครื่องมือ Viewer หลักจะเป็นมาร์กอัปของโฮสต์, มันถูกติดตั้งพร้อมกับ Viewer และไม่ควรอธิบายเป็นคอนโทรลแยก. สิ่งนี้ทำให้การจัดวาง, ป้ายกำกับ, ไอคอน, และกฎการอนุญาตอยู่ภายใต้การควบคุมของแอปพลิเคชันของคุณในขณะที่ทุกปุ่มทำงานกับอินสแตนซ์ 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>

แถบเครื่องมืออ้างอิงเต็มยังคัดลอกไฟล์ wwwroot/js/viewerToolbar.js ไปยังแอปพลิเคชันโฮสต์เพื่อใช้สำหรับการหมุน, แถบภาพย่อ, พิมพ์, เต็มจอ, การจัดวาง, และตัวช่วยสถานะปุ่ม. โหลดไฟล์โฮสต์นั้นหลังจาก Viewer.ReferenceScripts(...). เก็บตัวช่วยและมาร์กอัป <nav id="toolbar"> ของมันไว้ด้วยกันเมื่อคัดลอกการสาธิตเต็ม.

รักษาลำดับการเริ่มต้นแพคเกจที่ใช้โดยทั้งสองแอปอ้างอิง:

  1. สร้าง CSS สำหรับ Viewer และโมดูลที่มีใบอนุญาต.
  2. เรนเดอร์แถบเครื่องมือ Viewer, การเมานท์ Search/Annotation, และการเมานท์ Viewer พร้อมกัน.
  3. สร้างสคริปต์สำหรับ Viewer และโมดูลที่มีใบอนุญาต.
  4. โหลดไฟล์ viewerToolbar.js ของแอปพลิเคชันโฮสต์.
  5. เริ่มต้น docViewer และเก็บ objViewer ที่ได้.
  6. เริ่มต้นริบบิ้น Search หรือ Annotation แต่ละอันที่มีใบอนุญาต.
  7. เรียก attach(objViewer) บนทุกริบบิ้น.
  8. เปิดเอกสารและเก็บโทเคนของมันสำหรับคำขอของ Viewer และโมดูล.

Doconut.TestApp.Distributed รักษาการจัดองค์ประกอบ UI นี้อย่างแม่นยำและตัวช่วยแถบเครื่องมือ Viewer เดียวกัน. ค่า access ที่เพิ่มเข้ามาและการตั้งค่าการลองใหม่แบบเรนเดอร์แบบอะซิงโครนัสเป็นของการส่งข้อมูลแบบกระจาย; พวกมันไม่เปลี่ยนวิธีการประกอบ Viewer, แถบเครื่องมือ, หรือริบบิ้น.

การป้องกันฝั่งเซิร์ฟเวอร์มีความสำคัญ: เมื่อความสามารถแบบตัวเลือกไม่พร้อมใช้งาน, สคริปต์ของมันจะไม่ถูกสร้าง, ดังนั้นฟังก์ชันปลั๊กอิน jQuery ของมันจะไม่มีอยู่.

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>

ทั้งสองคอมโพเนนท์ที่ฝังอยู่สร้าง DOM ของริบบิ้นของตนเอง. Search มีกลุ่ม Find, Options, และ Results. Annotation มีเครื่องมือการเขียน, การควบคุมสไตล์, การบันทึก, และการส่งออก/รูปภาพที่เป็นตัวเลือก. แถบเหล่านี้เปิดเผยเมธอด open(), close(), reset(), และ isOpen(); ควรเรียก attach(objViewer) หนึ่งครั้งหลังจากสร้าง.

ตัวอย่างด้านบนละเว้นการเรียกกลับของโฮสต์ที่เป็นตัวเลือกและจุดสิ้นสุดการส่งออก/รูปภาพของ Annotation เพื่อให้การเริ่มต้นเป็นอย่างน้อยที่สุด. ดู การค้นหา และ คำอธิบาย สำหรับการตั้งค่าฟีเจอร์ที่สมบูรณ์, หรือ ธีมที่กำหนดเอง เพื่อสไตล์หรือแทนที่แถบเครื่องมือ Viewer ที่เป็นของโฮสต์.

เปิดเอกสาร

ฝั่งเซิร์ฟเวอร์มีจุดเชื่อมต่อเดียว: เซอร์วิส Viewer ที่ฉีดเข้ามาจะเปิดเอกสารและคืนค่าโทเคนเซสชัน.

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

ไคลเอนต์ดึงโทเคนนั้นและส่งให้วิดเจ็ตด้วย objViewer.View(token):

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

ปิดเอกสาร

เรียก objViewer.Close() เมื่อผู้ใช้ออกจากตัวดูหรือเปิดเอกสารทดแทน. ในเวิร์กโฟลว์ที่ควบคุมโดยเซิร์ฟเวอร์, viewer.CloseDocument(token) จะลบเซสชันที่แคชไว้ทันที, ปิดการทำงานของเอนจินการเรนเดอร์, ลบเครื่องหมายความปลอดภัย, และเพิกถอนโทเคน. การหมดอายุแบบเลื่อนจะทำความสะอาดเช่นเดียวกันในภายหลัง, แต่การปิดอย่างชัดเจนแนะนำสำหรับเอกสารขนาดใหญ่.

กระบวนการร้องขอที่สมบูรณ์คือ:

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

ถือโทเคนเหมือนข้อมูลประจำตัวแบบ bearer: อย่าบันทึก, อย่าจัดเก็บ, ส่งให้วิดเจ็ตเท่านั้น. มันระบุเซสชันเอกสารที่ใช้งานอยู่บนเซิร์ฟเวอร์และจะหยุดทำงานเมื่อเซสชันนั้นหมดอายุ — เปิดเอกสารใหม่เพื่อรับโทเคนใหม่.

รันมัน

วางไฟล์ PDF ที่ wwwroot/files/Sample.pdf, รัน dotnet run, แล้วเปิดหน้าที่โฮสต์วิดเจ็ต. หน้าแรกจะเรนเดอร์ในตัวดูพร้อมแผงภาพย่อทางซ้าย. หากไม่แสดง, ดูที่ การแก้ไขปัญหา.

สิ่งที่คุณจะได้โดยไม่มีใบอนุญาต

การไม่มีใบอนุญาตจะไม่ทำให้เกิดข้อผิดพลาด. ตัวดูจะเรนเดอร์ตามปกติ, แต่ทุกหน้าจะมีลายน้ำการประเมิน. ดูที่ การตั้งค่าใบอนุญาต เพื่อทราบว่า Doconut ค้นหาใบอนุญาตอย่างไรและอะไรจะเปลี่ยนเมื่อพบ.

หน้านี้เป็นประโยชน์หรือไม่?