البدء السريع
اعرض مستندك الأول في دقائق
يأخذ هذا الدليل تطبيق ASP.NET Core من ملف Program.cs فارغ إلى مستند يُعرض في المتصفح: تسجيل الخادم، حزمة المشاهد الكاملة (شريط أدوات المشاهد، تثبيت المشاهد، وأشرطة البحث/التعليق الاختيارية)، مراجع الأصول، تهيئة العميل، فتح المستند، والتنفيذ.
يجب أن يستهدف تطبيق المضيف بيئة تشغيل تدعم netstandard2.1 — .NET Core 3.0 أو أحدث. التطبيقات المرجعية المرفقة مع هذا الإصدار تستهدف net8.0.
إعداد الخادم
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();يحافظ التطبيق المرجعي على شكل طلب DocImage.axd التاريخي ويثبت طبقة middleware للمستند على فرع مطابق. هذا هو الترتيب الذي يجب نسخه عندما تقوم بنقل تطبيق .NET Standard موجود إلى الأمام، لأن العميل يواصل استدعاء نفس العنوان URL كما كان دائمًا:
builder.Services.AddDoconut(options =>
{
options.UnsafeMode = false; // ابقِ فحص جلسة Doconut مفعلاً
options.ShowDoconutInfo = false;
});
builder.Services.AddDistributedMemoryCache(); // تحتاج حالة الجلسة إلى ذاكرة تخزين مؤقت (MVC/Razor Pages يضيفها بالفعل)
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(3600);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// مطلوب لتشفيرات متعددة البايت في بعض صيغ المستندات.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseDoconutResources(); // قبل فرع المستند
app.MapWhen(
context => context.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
branch => branch.UseDoconut());UseDoconut() لا يأخذ أي خيارات في هذا الإصدار — كل شيء تم نقله إلى AddDoconut(). MiddlewarePath هو قيمة تنسيق؛ لا يربط فرع ASP.NET Core بحد ذاته. ResourcesPath يخدم الحزمة المدمجة عند /doconut-res، وبالتالي مسار مورد صورة الواجهة هو ResPath: 'doconut-res/images'.
إضافة المشاهد إلى صفحة
المشاهد هو النواة المطلوبة للصفحة. سطح العرض الخاص به يستخدم عنصرين div متداخلين:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>عامل شريط الأدوات، تثبيت الوحدات، وسطح المشاهد كأنها تركيبة صفحة واحدة. يحقن البحث والتعليق أشرطةهما المدمجة في التثبيتات الاختيارية، لكن هذه الوحدات لا تكون مستقلة أبداً: فهي دائمًا تُرفق بالمشاهد على نفس الصفحة.
<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 قبل سكريبتات المشاهد:
@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 بحذف موارد الوحدة الاختيارية عندما لا تمنح الرخصة الحالية تلك القدرة؛ يظل المشاهد الأساسي يبدأ.
هذه الاستدعاءات تستبدل ملفات السكريبت المنسوخة يدويًا. إذا كان التطبيق لا يزال يحمل ملف docViewer.js أو documentLinks.js أو docViewer.UI.js الخاص به، احذف تلك الوسوم بمجرد أن تعمل الصفحة باستخدام الموارد الصادرة — راجع دليل الترحيل.
تهيئة المشاهد
الودجت على جانب العميل هو مكوّن jQuery. هذه مجموعة الحد الأدنى من خيارات التهيئة الفعلية (وليست شفرة تجريبية):
let searchBar = null;
let annBar = null;
const objViewer = $('#div_ctlDoc').docViewer({
showThumbs: true,
autoLoad: false,
pageZoom: 100,
FitType: 'width',
BasePath: '/',
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);
}
});تفصيلان يحددان ما إذا كانت هذه الصفحة تعمل على الإطلاق:
- الاستدعاءات الراجعة هي خيارات.
onViewerReadyوonViewerBusyوonCopyوonAutoLoadStatusتُمرَّر في كائن الخيارات. المشاهد السابق كان يستخلص أسماء الدوال العالمية من معرف الحاوية ويستدعيها؛ هذا لا يفعل ذلك. المعالج المعلن كدالة عالمية فقط لا يُنفّذ أبداً، ولا يُبلغ عن أي شيء. ResPathيشير إلى بادئة الموارد، وليس جذر التطبيق. معResourcesPathالافتراضي، تكون القيمةdoconut-res/images. إذا غيرتResourcesPath، استخدم بادئتك الخاصة بدلاً من ذلك.
كلاهما يفشل بصمت — راجع استكشاف الأخطاء وإصلاحها ودليل الترحيل.
حالة الأحرف في الخيارات مختلطة فعليًا — showThumbs و autoLoad و pageZoom هي camelCase، بينما FitType و BasePath و ResPath هي PascalCase. لا توجد قاعدة ثابتة؛ إذا أخطأت في حالة الأحرف سيتم تجاهل الخيار بصمت (سيعود الودجت إلى الإعداد الافتراضي بدلاً من إلقاء استثناء).
تجميع حزمة المشاهد الكاملة
كل من تطبيقات المرجع تثبت الأجزاء التالية معًا في صفحة واحدة:
| جزء من الحزمة | المتطلبات | كيفية الاتصال |
|---|---|---|
موارد المشاهد، التثبيت، و objViewer | مطلوب | عارض المستند الأساسي |
| شريط أدوات المشاهد | مطلوب في تركيب المرجع | علامات HTML للمضيف؛ الأزرار تستدعي نفس objViewer |
| شريط البحث | اختياري، وحدة مرخصة | doconutSearchBar(...).attach(objViewer) |
| شريط التعليقات | اختياري، وحدة مرخصة | doconutAnnotationBar(...).attach(objViewer) |
على الرغم من أن شريط أدوات المشاهد الرئيسي هو علامات 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>حافظ على ترتيب تهيئة الحزمة المستخدم من قبل كل من تطبيقات المرجع:
- إصدار CSS للمشاهد والوحدات المرخصة.
- عرض شريط أدوات المشاهد، تثبيت البحث/التعليقات، وتثبيت المشاهد معًا.
- إصدار السكريبتات للمشاهد والوحدات المرخصة.
- تحميل سكريبت المساعدة الخاص بشريط أدوات تطبيق المضيف.
- تهيئة
docViewerوالاحتفاظ بـobjViewerالناتج. - تهيئة كل شريط بحث أو تعليقات مرخص.
- استدعاء
attach(objViewer)على كل شريط. - فتح المستند والاحتفاظ برمز الوصول الخاص به لطلبات المشاهد والوحدات.
الحمايات على جانب الخادم مهمة: عندما تكون قدرة اختيارية غير متاحة، لا يتم إصدار سكريبتها، وبالتالي لا توجد دالة مكوّن 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) مرة واحدة بعد إنشائها.
المثال أعلاه يحذف استدعاءات المضيف الاختيارية ونقاط النهاية لتصدير/صورة التعليقات لتقليل بدء التشغيل. راجع البحث والتعليقات للإعداد الكامل الخاص بالميزات، أو الثيمات المخصصة لتصميم أو استبدال شريط أدوات المشاهد المملوك للمضيف.
فتح مستند
جانب الخادم هو نقطة نهاية واحدة: خدمة 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);
});لا يوجد شيء لتسخينه قبل الفتح الأول. إذا كنت تنقل تطبيقاً إلى الأمام، احذف أي استدعاء لـ InitCache() والعلم الذي يحميه — الطريقة لم تعد موجودة.
إغلاق المستند
استدعِ 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 ترخيصاً وما الذي يتغير بمجرد العثور عليه.
هل كانت هذه الصفحة مفيدة؟