התחלה מהירה
הצגת המסמך הראשון שלך בתוך דקות
מדריך זה מעביר אפליקציית ASP.NET Core מריק Program.cs למסמך שמוצג בדפדפן: רישום השרת, חבילת הצופה המלאה (סרגל כלים של הצופה, הרכבה של הצופה, ורצועות חיפוש/הערות אופציונליות), הפניות למשאבים, אתחול הלקוח, פתיחת המסמך והפעלה.
הגדרת השרת
AddDoconut() רושמת את השירותים; UseDoconutResources() ו-UseDoconut() מחברות את המידלוור. קריאת המשאבים חייבת לבוא ראשונה. קריאות הסשן נדרשות גם הן — האבטחה המוגדרת של 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'. כתבו את שני הערכים בלי קו נטוי מוביל: הצופה מוסיף אחד בעצמו, ולכן '/doconut' יהפוך ל‑//doconut/DocImage.axd, שהדפדפן קורא זאת כבקשה למארח בשם doconut, והצופה נכשל עם ההודעה "The viewer handler could not be reached".
הוספת הצופה לעמוד
הצופה הוא הליבה הנדרשת של העמוד. משטח הרינדור שלו משתמש בשני div מקוננים:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>חשבו על סרגל הכלים, הרכבות המודול, ומשטח הצופה כקומפוזיציה של עמוד אחד. חיפוש והערות מכניסים את הרצועות המשובצות שלהם למקומות הרכבה אופציונליים, אך מודולים אלה אף פעם אינם עצמאיים: הם תמיד מצורפים לצופה באותו העמוד. השתמשו באותו סדר כמו ב-Doconut.TestApp וב-Doconut.TestApp.Distributed:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- ניווט הצופה, זום, חיפוש וכפתורי הערה -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>הפנייה למשאבי הצופה
בתצוגת Razor, שירות Viewer המוזרק מפיק את תגיות <link> ו-<script> של הצופה בסדר תלות — הווידג'ט הוא תוסף jQuery, ולכן יש לטעון את jQuery לפני סקריפטי הצופה:
@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
}))לקבלת חבילת הצופה המלאה, בקשו את משאבי הצופה והמודול יחד:
@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 הם דגלים ליבה מחויבים. לעולם אל תפרסמו דוגמת רצועת חיפוש או הערה ללא אלה, ללא הרכבת הצופה, וללא מופע docViewer. ReferenceCss ו-ReferenceScripts משאירים חוץ משאבי מודול אופציונלי כאשר הרישיון הנוכחי אינו מעניק את היכולת; הצופה הליבה עדיין מתחיל.
אתחול הצופה
הווידג'ט בצד הלקוח הוא תוסף 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. אין כלל עקבי; אם מקישים קייסינג שגוי האפשרות תזולק בשקט (הווידג'ט יחזור לערך ברירת המחדל במקום לזרוק שגיאה).
הרכבת חבילת הצופה המלאה
שתי יישומי ההפניה של .NET 6 מתקינים את החלקים הבאים יחד בדף אחד:
| חלק מהחבילה | דרישה | איך זה מחובר |
|---|---|---|
משאבי הצופה, הרכבה, ו‑objViewer | נדרש | מעבד המסמכים המרכזי |
| סרגל כלים של הצופה | נדרש בהרכבה הרפרנסית | סימון מארח; הכפתורים קוראים ל‑objViewer זהה |
| ריבון חיפוש | אופציונלי, מודול מורשה | doconutSearchBar(...).attach(objViewer) |
| ריבון הערות | אופציונלי, מודול מורשה | doconutAnnotationBar(...).attach(objViewer) |
למרות שסרגל הכלים הראשי של הצופה הוא סימון מארח, הוא מותקן לצד הצופה ולעולם אין לתעד אותו כ‑control מבודד. כך נשמרים הפריסה, התוויות, האיקונים, וכללי האימות תחת שליטת האפליקציה שלך, בעוד שכל כפתור מניע את אותה מופע של הצופה:
<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"> שלו יחד כאשר מעתיקים את מימוש ההדגמה המלא.
שמור על סדר האתחול של החבילה כפי שמשתמשים בו שני יישומי ההפניה:
- הפץ CSS עבור הצופה והמודולים המורשים.
- הצג את סרגל הכלים של הצופה, הרכבות חיפוש/הערות, והרכבת הצופה יחד.
- הפץ סקריפטים עבור הצופה והמודולים המורשים.
- טען את
viewerToolbar.jsשל אפליקציית המארח. - אתחל את
docViewerושמור את ה‑objViewerשנוצר. - אתחל כל ריבון חיפוש או הערות מורשה.
- קרא
attach(objViewer)על כל ריבון. - פתח את המסמך ושמור את האסימון שלו עבור בקשות הצופה והמודול.
Doconut.TestApp.Distributed שומר על הרכבה מדויקת זו של UI ועל אותו עזר סרגל כלים של הצופה. ערך הבקשה access הנוסף שלו והגדרות הניסיון החוזר של רינדור אסינכרוני שייכים לתחבורה המבוזרת; הם אינם משנים את האופן שבו הצופה, סרגל הכלים או הריבונים מורכבים.
הגנות בצד השרת חשובות: כאשר יכולת אופציונלית אינה זמינה, הסקריפט שלה אינו מפורסם, ולכן פונקציית תוסף ה‑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) פעם אחת לאחר יצירתם.
הדוגמה שלמעלה משאירה מחוץ לקולבקרי המארח האופציונליים וקצות ה‑export/image של ההערות כדי לשמור על אתחול מינימלי. ראה חיפוש ו-הערות עבור ההגדרה המלאה של תכונה ספציפית, או ערכות נושא מותאמות כדי לעצב או להחליף את סרגל הכלים של הצופה שבבעלות המארח.
פתח מסמך
צד השרת הוא נקודת קצה אחת: שירות Viewer המוזרק פותח את המסמך ומחזיר אסימון סשן.
app.MapPost("/api/open", async (Viewer viewer) =>
{
// האסימון אינו שקוף — העבר אותו לווידג'ט, אל תתעד או תשמר אותו.
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { 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) מסיר מיידית את הסשן השמור במטמון, משחרר את מנוע הרינדור, מוחק את סימן האבטחה שלו, ומבטל את האסימון. תפוגה מתגלגלת מבצעת את ניקוי זה לבסוף, אך סגירה מפורשת מומלצת למסמכים גדולים.
זרימת הבקשה המלאה היא:
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 מוצא רישיון ומה משתנה לאחר שמצא אותו.
האם דף זה היה מועיל?