Bắt đầu nhanh

Hiển thị tài liệu đầu tiên của bạn trong vài phút

Hướng dẫn này đưa một ứng dụng ASP.NET Core từ một Program.cs trống đến một tài liệu được hiển thị trong trình duyệt: đăng ký máy chủ, gói Viewer đầy đủ (thanh công cụ Viewer, gắn Viewer và các ribbon Tìm kiếm/Chú thích tùy chọn), tham chiếu tài nguyên, khởi tạo client, mở tài liệu và thực thi.

Cài đặt máy chủ

AddDoconut() đăng ký các dịch vụ; UseDoconutResources() và UseDoconut() kết nối middleware. Lệnh gọi tài nguyên phải được đặt trước. Các lệnh gọi session cũng cần thiết — bảo mật tài liệu mặc định của Doconut xác thực mỗi yêu cầu trang dựa trên trạng thái session của ASP.NET. Trạng thái session cần một bộ nhớ cache: AddDistributedMemoryCache() cung cấp một. Ứng dụng MVC hoặc Razor Pages đã đăng ký sẵn, nhưng một ứng dụng minimal-API thì không, và nếu thiếu sẽ khiến ứng dụng dừng khởi động với lỗi Unable to resolve service for type 'IDistributedCache'. Đã đăng ký Doconut trong phần Cài đặt chưa? Bỏ qua và chuyển đến phần tiếp theo.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // trạng thái session cần một cache (MVC/Razor Pages đã thêm sẵn một)
builder.Services.AddSession(); // bảo mật tài liệu Doconut dựa trên trạng thái session của ASP.NET

app.UseSession();          // gọi UseSession() trước UseDoconut()
app.UseDoconutResources(); // phải được đăng ký trước UseDoconut()
app.UseDoconut();

Đối với cấu trúc đường dẫn kiểu sản xuất, ánh xạ middleware tài liệu tới một nhánh cụ thể và giữ bốn cài đặt đường dẫn đồng nhất:

csharp
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 là một giá trị phối hợp; nó không tự ánh xạ một nhánh ASP.NET Core. Trong ví dụ này máy chủ ánh xạ /doconut, vì vậy client sử dụng BasePath: 'doconut'. ResourcesPath cung cấp gói nhúng tại /doconut-res, và đường dẫn tài nguyên hình ảnh của widget do đó là ResPath: 'doconut-res/images'. Viết cả hai không có dấu gạch chéo đầu: viewer sẽ tự thêm một dấu gạch chéo, vì vậy '/doconut' sẽ trở thành //doconut/DocImage.axd, trình duyệt sẽ hiểu đó là một yêu cầu tới máy chủ có tên doconut, và viewer sẽ thất bại với thông báo "The viewer handler could not be reached".

Thêm viewer vào trang

Viewer là phần lõi bắt buộc của trang. Bề mặt hiển thị của nó sử dụng hai div lồng nhau:

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

Xem thanh công cụ, các điểm gắn mô-đun và bề mặt Viewer như một thành phần trang duy nhất. Tìm kiếm và Chú thích chèn các ribbon nhúng của chúng vào các điểm gắn tùy chọn, nhưng các mô-đun này không bao giờ độc lập: chúng luôn được gắn vào Viewer trên cùng một trang. Sử dụng cùng thứ tự như Doconut.TestApp và 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>

Tham chiếu tài nguyên viewer

Trong một Razor view, dịch vụ Viewer được tiêm sẽ phát ra các thẻ <link> và <script> của viewer theo thứ tự phụ thuộc — widget là một plugin jQuery, vì vậy jQuery phải được tải trước các script của viewer:

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
}))

Đối với gói Viewer đầy đủ, yêu cầu cả tài nguyên Viewer và các mô-đun cùng lúc:

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 và IncludeViewerScripts là các cờ lõi bắt buộc. Không bao giờ công bố một ví dụ Ribbon Tìm kiếm hoặc Chú thích mà không có chúng, gắn Viewer, và một thể hiện docViewer. ReferenceCss và ReferenceScripts sẽ bỏ qua tài nguyên của mô-đun tùy chọn khi giấy phép hiện tại không cho phép tính năng đó; Viewer lõi vẫn sẽ khởi động.

Khởi tạo trình xem

Widget phía máy khách là một plugin jQuery. Đây là một tập hợp tối thiểu các tùy chọn khởi tạo thực (không phải pseudocode):

javascript
let searchBar = null;
let annBar = null;

const objViewer = $('#div_ctlDoc').docViewer({
    showThumbs: true,
    autoLoad:   false,
    pageZoom:   100,
    FitType:    'width',
    BasePath:   'doconut',             // không có dấu '/' ở đầu
    ResPath:    'doconut-res/images',  // không có dấu '/' ở đầu
    onViewerReady: function () {
        // các trang đã hiển thị; có thể ẩn biểu tượng tải ở đây
    },
    // Chuyển tiếp các sự kiện vòng đời chú thích tới ribbon nhúng khi nó có mặt.
    onAnnLoaded:    () => annBar?.handleAnnLoaded(),
    onAnnSaved:     () => annBar?.handleAnnSaved(),
    onAnnSaveError: () => annBar?.handleAnnSaveError(),
    onAnnClosed:    () => annBar?.handleAnnClosed(),
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

Cách viết chữ hoa cho các tùy chọn thực sự hỗn hợp — showThumbs, autoLoad và pageZoom là camelCase, nhưng FitType, BasePath và ResPath là PascalCase. Không có quy tắc nhất quán; nếu viết sai kiểu chữ, tùy chọn sẽ bị bỏ qua một cách im lặng (widget sẽ quay lại giá trị mặc định thay vì ném lỗi).

Tập hợp gói Viewer hoàn chỉnh

Cả hai ứng dụng tham chiếu .NET 6 đều cài đặt các phần sau cùng nhau trên một trang:

Phần của góiYêu cầuCách kết nối
Tài nguyên Viewer, mount, và objViewerBắt buộcBộ render tài liệu lõi
Thanh công cụ ViewerBắt buộc trong cấu hình tham chiếuMarkup của host; các nút gọi cùng objViewer
Ribbon tìm kiếmTùy chọn, mô-đun có giấy phépdoconutSearchBar(...).attach(objViewer)
Ribbon chú thíchTùy chọn, mô-đun có giấy phépdoconutAnnotationBar(...).attach(objViewer)

Mặc dù thanh công cụ Viewer chính là markup của host, nó được cài đặt cùng với Viewer và không bao giờ được tài liệu hoá như một điều khiển riêng lẻ. Điều này giữ cho bố cục, nhãn, biểu tượng và các quy tắc ủy quyền của nó dưới sự kiểm soát của ứng dụng của bạn trong khi mọi nút đều điều khiển cùng một thể hiện 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>

Thanh công cụ tham chiếu đầy đủ cũng sao chép wwwroot/js/viewerToolbar.js vào ứng dụng để hỗ trợ quay, thu nhỏ, in, toàn màn hình, bố cục và các trợ giúp trạng thái nút. Hãy tải tệp này sau Viewer.ReferenceScripts(...). Giữ trợ giúp và markup <nav id="toolbar"> của nó cùng nhau khi sao chép triển khai demo đầy đủ.

Giữ thứ tự khởi tạo gói được cả hai ứng dụng tham chiếu sử dụng:

  1. Phát ra CSS cho Viewer và các mô-đun có giấy phép.
  2. Render thanh công cụ Viewer, các mount Search/Annotation, và mount Viewer cùng nhau.
  3. Phát ra script cho Viewer và các mô-đun có giấy phép.
  4. Tải viewerToolbar.js của ứng dụng.
  5. Khởi tạo docViewer và giữ lại objViewer được tạo.
  6. Khởi tạo mỗi Ribbon Search hoặc Annotation có giấy phép.
  7. Gọi attach(objViewer) trên mọi Ribbon.
  8. Mở tài liệu và giữ token của nó cho các yêu cầu Viewer và mô-đun.

Doconut.TestApp.Distributed giữ nguyên bố cục UI này và cùng trợ giúp thanh công cụ Viewer. Giá trị yêu cầu access bổ sung và các cài đặt retry render bất đồng bộ thuộc về giao thức phân phối; chúng không thay đổi cách Viewer, thanh công cụ, hoặc các Ribbon được lắp ráp.

Các biện pháp bảo vệ phía máy chủ rất quan trọng: khi một khả năng tùy chọn không khả dụng, script của nó sẽ không được phát ra, vì vậy hàm plugin jQuery của nó sẽ không tồn tại.

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>

Cả hai thành phần nhúng đều tạo ra DOM Ribbon riêng của chúng. Search chứa các nhóm Find, Options và Results. Annotation chứa các công cụ tạo, điều khiển kiểu, hành động lưu và các hành động xuất/ảnh tùy chọn. Các thanh này cung cấp open(), close(), reset(), và isOpen(); luôn gọi attach(objViewer) một lần sau khi tạo chúng.

Ví dụ trên bỏ qua các callback tùy chọn của host và các endpoint xuất/ảnh của Annotation để giữ cho việc khởi động tối thiểu. Xem Tìm kiếm và Chú thích để biết cài đặt chi tiết cho từng tính năng, hoặc Chủ đề tùy chỉnh để tạo kiểu hoặc thay thế thanh công cụ Viewer do host sở hữu.

Mở một tài liệu

Phía máy chủ có một endpoint: dịch vụ Viewer được tiêm sẽ mở tài liệu và trả về một token phiên.

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // Token là không trong suốt — chuyển nó cho widget, không bao giờ ghi log hoặc lưu trữ.
    string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

Phía client lấy token đó và chuyển cho widget bằng objViewer.View(token):

javascript
fetch('/api/open', { method: 'POST' })
    .then(resp => resp.json())
    .then(data => {
        currentToken = data.token;
        objViewer.View(currentToken);
    });

Đóng tài liệu

Gọi objViewer.Close() khi người dùng rời khỏi viewer hoặc mở một tài liệu thay thế. Trong các quy trình làm việc do máy chủ điều khiển, viewer.CloseDocument(token) sẽ ngay lập tức xóa phiên đã lưu trong bộ nhớ cache, giải phóng engine render, xóa dấu bảo mật và thu hồi token. Thời gian hết hạn trượt (sliding expiration) cuối cùng cũng thực hiện việc dọn dẹp tương tự, nhưng việc đóng rõ ràng được khuyến nghị cho các tài liệu lớn.

Luồng yêu cầu hoàn chỉnh là:

text
AddDoconut + middleware
    -> render CSS/scripts and mount div
    -> initialize docViewer
    -> OpenDocumentAsync
    -> return opaque token
    -> objViewer.View(token)
    -> page/search/annotation requests
    -> Close / CloseDocument

Xem token như một chứng chỉ mang: không bao giờ ghi log, không bao giờ lưu trữ, chỉ chuyển nó cho widget. Nó xác định một phiên tài liệu đang hoạt động trên máy chủ và sẽ ngừng hoạt động khi phiên đó hết hạn — mở lại tài liệu để nhận token mới.

Chạy nó

Đặt một file PDF tại wwwroot/files/Sample.pdf, chạy dotnet run, và mở trang chứa widget. Trang đầu tiên sẽ được render trong viewer, với một bảng thu nhỏ ở phía trái. Nếu không, xem Khắc phục sự cố.

Bạn nhận được gì khi không có giấy phép

Một giấy phép thiếu sẽ không gây lỗi. Viewer vẫn render bình thường, nhưng mỗi trang sẽ có một dấu nước đánh giá. Xem Cài đặt giấy phép để biết Doconut tìm giấy phép như thế nào và những gì sẽ thay đổi khi nó tìm được.

Trang này có hữu ích không?