البدء السريع

اعرض مستندك الأول في دقائق

هذا الدليل يأخذ تطبيق ASP.NET Core من Program.cs فارغ إلى مستند يُعرض في المتصفح: تسجيل الخادم، حزمة العارض الكاملة (شريط أدوات العارض، تركيب العارض، وأشرطة البحث/التعليقات التوضيحية الاختيارية)، مراجع الأصول، تهيئة العميل، فتح المستند، والتنفيذ.

إعداد الخادم

AddDoconut() يسجل الخدمات؛ UseDoconutResources() و UseDoconut() يربطان الطبقة الوسيطة. يجب أن يكون استدعاء الموارد أولاً. استدعاءات الجلسة مطلوبة أيضًا — أمان المستند الافتراضي لـ Doconut يتحقق من كل طلب صفحة مقابل حالة جلسة 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();

لترتيب مسارات على نمط الإنتاج، قم بربط طبقة وساطة المستند بفرع صريح واحفظ إعدادات المسارات الأربعة متناسقة:

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'.

إضافة العارض إلى صفحة

العارض هو النواة المطلوبة للصفحة. سطح العرض يستخدم عنصرين div متداخلين:

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

عامل شريط الأدوات، تركيبات الوحدات، وسطح العارض كأنها تركيبة صفحة واحدة. يحقن البحث والتعليقات التوضيحية أشرطةهم المدمجة في تركيبات اختيارية، لكن هذه الوحدات لا تكون مستقلة أبداً: فهي دائمًا تُرفق بالعارض في نفس الصفحة. استخدم نفس الترتيب كما في 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
}))

لطلب حزمة العارض الكاملة، اطلب موارد العارض والوحدات معًا:

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 هما العلامتان الأساسيتان الإجباريتان. لا تنشر مثال شريط البحث أو التعليقات التوضيحية بدونهما، دون تركيب العارض، ودون كائن docViewer. ReferenceCss و ReferenceScripts يتغاضيان عن موارد وحدة اختيارية عندما لا تمنح الرخصة الحالية تلك القدرة؛ يظل العارض الأساسي يعمل.

تهيئة العارض

الودجة على جانب العميل هي مكوّن إضافي لـ 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. لا توجد قاعدة ثابتة؛ إذا كتبتها بشكل غير صحيح سيتجاهل العارض الخيار صامتًا (ستعود الودجة إلى الإعداد الافتراضي بدلاً من إلقاء استثناء).

تجميع حزمة العارض الكاملة

كل من تطبيقات المرجع .NET 8 تثبت الأجزاء التالية معًا في صفحة واحدة:

جزء من الحزمةالمتطلبكيفية الاتصال
موارد العارض، التركيب، و objViewerمطلوبعارض المستند الأساسي
شريط أدوات العارضمطلوب في التركيبة المرجعيةعلامات HTML للمضيف؛ الأزرار تستدعي نفس objViewer
شريط البحثاختياري، وحدة مرخصةdoconutSearchBar(...).attach(objViewer)
شريط التعليقات التوضيحيةاختياري، وحدة مرخصةdoconutAnnotationBar(...).attach(objViewer)

على الرغم من أن شريط أدوات العارض الرئيسي هو علامة HTML للمضيف، فإنه يُثبت جنبًا إلى جنب مع العارض ولا يجب توثيقه كعنصر منفصل. هذا يحافظ على تخطيطه، تسمياته، أيقوناته، وقواعد التفويض تحت سيطرة تطبيقك بينما كل زر يتحكم في نفس نسخة العارض:

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. إصدار موارد العارض، البحث، والتعليقات التوضيحية معًا.
  2. عرض شريط أدوات العارض، تركيبات الشرائط، وتركيب العارض معًا.
  3. تهيئة docViewer أولاً.
  4. إنشاء كل شريط مرخص وإرفاقه بنفس objViewer.
  5. فتح المستند والاحتفاظ بالرمز المميز لطلبات الوحدات.

Doconut.TestApp.Distributed يحافظ على هذه التركيبة الدقيقة للواجهة ومساعد شريط أدوات العارض نفسه. قيمة طلب access الإضافية وإعدادات إعادة المحاولة غير المتزامنة تنتمي إلى النقل الموزع؛ لا تغير طريقة تجميع العارض أو شريط الأدوات أو الشرائط.

الحراس على جانب الخادم مهمة: عندما تكون قدرة اختيارية غير متوفرة، لا يُصدر سكريبتها، وبالتالي لا توجد دالة مكوّن إضافي لـ 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 المُحقنة تفتح المستند وتعيد رمز جلسة.

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 وما يتغيّر بمجرد العثور عليه.

هل كانت هذه الصفحة مفيدة؟