התחלה מהירה

הצג את המסמך הראשון שלך בתוך דקות

מדריך זה מעביר אפליקציית ASP.NET Core מקובץ Program.cs ריק למסמך שמוצג בדפדפן: רישום השרת, חבילת ה-Viewer המלאה (סרגל כלים של Viewer, הרכבת Viewer, ורצועות חיפוש/הערות אופציונליות), הפניות למשאבים, אתחול הלקוח, פתיחת המסמך והפעלה.

הגדרת השרת

AddDoconut() רושמת את השירותים; UseDoconutResources() ו-UseDoconut() מחברות את ה-middleware. קריאת המשאבים חייבת לבוא ראשונה. קריאות ה-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 הוא הליבה הדרושה של העמוד. משטח הרינדור שלו משתמש בשני divs מקוננים:

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

חשבו את סרגל הכלים, הרכבות המודול, ומשטח ה-Viewer כקומפוזיציה אחת של העמוד. חיפוש והערות מכניסים את הרצועות המוטמעות שלהם למקומות אופציונליים, אך מודולים אלו אף פעם אינם עומדים לבד: הם תמיד מצורפים ל-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, שירות Viewer המוזרק מפיק את תגיות <link> ו-<script> של הצופה בסדר תלות — הווידג'ט הוא תוסף jQuery, ולכן יש לטעון את jQuery לפני סקריפטי הצופה:

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 הם דגלים ליבה מחויבים. לעולם אל תפרסמו דוגמת רצועת חיפוש או הערות ללא אלה, ללא הרכבת ה-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 8, מתקינים את החלקים הבאים יחד בעמוד אחד:

חלק מהחבילהדרישהאיך זה מחובר
משאבי 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. הפיצו יחד משאבי Viewer, חיפוש והערות.
  2. הציגו יחד את סרגל הכלים של Viewer, הרכבות הרצועות, והרכבת ה-Viewer.
  3. אתחלו תחילה את docViewer.
  4. צרו כל רצועה ברישיון וצרפו אותה לאותו objViewer.
  5. פתחו את המסמך ושמרו את הטוקן שלו לבקשות המודול.

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 של הרצועה שלהם. חיפוש מכיל קבוצות Find, Options, ו-Results. הערות מכילות את כלי הכתיבה, בקרות הסגנון, פעולות השמירה, ופעולות ייצוא/תמונה אופציונליות. הברים מציגים את open(), close(), reset(), ו-isOpen(); תמיד קראו attach(objViewer) פעם אחת לאחר יצירתם.

הדוגמה למעלה משאירה ללא תוספות קריאות חזרה של המארח וכתובות קצה לייצוא/תמונות של הערות כדי לשמור על אתחול מינימלי. ראו חיפוש ו-הערות לקבלת ההגדרה המלאה של הפיצ'רים, או ערכות נושא מותאמות כדי לעצב או להחליף את סרגל הכלים של 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

התייחסו לטוקן כמו לאישור נושא: אל תתעדו אותו, אל תשמרו אותו, העבירו אותו רק לווידג'ט. הוא מזהה סשן מסמך חי על השרת ומפסיק לעבוד כאשר הסשן פג — פתחו מחדש את המסמך לקבלת טוקן חדש.

הרצת הפרויקט

שים קובץ PDF ב-wwwroot/files/Sample.pdf, הפעל dotnet run, ופתח את העמוד שמארח את הווידג'ט. העמוד הראשון מוצג בצופה, עם פאנל תמונות ממוזערות בצד שמאל. אם זה לא קורה, ראו פתרון בעיות.

מה שמקבלים ללא רישיון

רישיון חסר אינו גורם לשגיאה. הצופה מציג כרגיל, אך כל עמוד נושא סימן מים של הערכה. ראו הגדרת רישיון כדי להבין איך Doconut מוצא רישיון ומה משתנה כאשר הוא נמצא.

האם דף זה היה מועיל?