البدء السريع
اعرض مستندك الأول في دقائق
هذا الشرح يأخذ تطبيق 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 يتغاضيان عن موارد وحدة اختيارية عندما لا تسمح الرخصة الحالية بذلك؛ يظل العارض الأساسي يعمل.
تهيئة العارض
الودجت على جانب العميل هو مكوّن إضافي لجـQuery. هذه مجموعة الحد الأدنى من خيارات التهيئة الفعلية (ليس كودًا تجريديًا):
let searchBar = null;
let annBar = null;
const objViewer = $('#div_ctlDoc').docViewer({
showThumbs: true,
autoLoad: false,
pageZoom: 100,
FitType: 'width',
BasePath: 'doconut', // no leading '/'
ResPath: 'doconut-res/images', // no leading '/'
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. لا توجد قاعدة ثابتة؛ إذا تم كتابة الأحرف بشكل غير صحيح سيتم تجاهل الخيار صامتًا (يعود الودجت إلى الإعداد الافتراضي بدلاً من إلقاء خطأ).
تجميع حزمة العارض الكاملة
كل من تطبيقات المرجعية .NET 8 تثبت الأجزاء التالية معًا في صفحة واحدة:
| جزء من الحزمة | المتطلبات | كيفية الاتصال |
|---|---|---|
موارد العارض، التركيب، و objViewer | مطلوب | عارض المستند الأساسي |
| شريط أدوات العارض | مطلوب في تركيبة المرجعية | علامات الاستضافة؛ الأزرار تستدعي نفس objViewer |
| شريط البحث | اختياري، وحدة مرخصة | doconutSearchBar(...).attach(objViewer) |
| شريط التعليقات | اختياري، وحدة مرخصة | doconutAnnotationBar(...).attach(objViewer) |
على الرغم من أن شريط أدوات العارض الرئيسي هو علامات استضافة، فإنه يُثبت جنبًا إلى جنب مع العارض ولا يجب أبدًا توثيقه كعنصر تحكم منفصل. هذا يحافظ على تخطيطه، تسمياته، أيقوناته، وقواعد التفويض تحت سيطرة تطبيقك بينما كل زر يتحكم في نفس نسخة العارض:
<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"> معًا عند نسخ تنفيذ العرض الكامل.
حافظ على ترتيب تهيئة الحزمة المستخدم في كل من تطبيقات المرجعية:
- إصدار موارد العارض، البحث، والتعليقات معًا.
- عرض شريط أدوات العارض، تركيبات الشريط، وتركيب العارض معًا.
- تهيئة
docViewerأولاً. - إنشاء كل شريط مرخص وإرفاقه بنفس
objViewer. - فتح المستند والاحتفاظ بالرمز المميز الخاص به لطلبات الوحدات.
Doconut.TestApp.Distributed يحافظ على هذا التكوين الدقيق للواجهة ومساعد شريط أدوات العارض نفسه. قيمة طلب access الإضافية وإعدادات إعادة المحاولة للعرض غير المتزامن تخص النقل الموزع؛ فهي لا تغير طريقة تجميع العارض أو شريط الأدوات أو الأشرطة.
حراس الجانب الخادمي مهمة: عندما تكون القدرة الاختيارية غير متاحة، لا يتم إصدار سكريبتها، وبالتالي لا توجد دالة jQuery plug-in لها.
<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) مرة واحدة بعد إنشائها.
يتجاهل المثال أعلاه استدعاءات رد الفعل الاختيارية للمستضيف ونقاط النهاية لتصدير/صورة التعليقات لتقليل الإقلاع. راجع Search وAnnotations لإعداد كامل للميزات المحددة، أو Custom Themes لتصميم أو استبدال شريط أدوات العارض المملوك للمستضيف.
فتح مستند
جانب الخادم هو نقطة نهاية واحدة: خدمة 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 ترخيصًا وما الذي يتغير بمجرد العثور عليه.
هل كانت هذه الصفحة مفيدة؟