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 file Program.cs trống tới 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, phần 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ấu hình máy chủ
AddDoconut() đăng ký các dịch vụ; UseDoconutResources() và UseDoconut() kết nối middleware. Lệnh gọi resources 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 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 không có, ứng dụng sẽ dừng khi khởi động với lỗi Unable to resolve service for type 'IDistributedCache'. Đã đăng ký Doconut trong phần Cài đặt? Bỏ qua và chuyển tới phần tiếp theo.
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)
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 thiết lập đường dẫn đồng bộ:
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 phục vụ 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 đây là yêu cầu tới một host 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 cốt 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:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Xem thanh công cụ, các phần gắn module 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 phần gắn tùy chọn, nhưng các module này không bao giờ độc lập: chúng luôn 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:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Điều hướng trình xem, thu phóng, các nút Tìm kiếm và Chú thích -->
</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:
@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
}))Để lấy gói Viewer đầy đủ, yêu cầu cả tài nguyên Viewer và các module cùng lúc:
@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ờ cốt lõi bắt buộc. Không bao giờ công bố ví dụ Ribbon Tìm kiếm hoặc Chú thích mà không có chúng, phần gắn Viewer, và một thể hiện docViewer. ReferenceCss và ReferenceScripts sẽ bỏ qua tài nguyên của module tùy chọn khi giấy phép hiện tại không cho phép; Viewer cốt 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 mã giả):
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ị; an toàn để ẩ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);
}
});Việc viết hoa chữ cái trong 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ữ thì 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 đầy đủ
Cả hai ứng dụng tham chiếu .NET 8 đều cài đặt các phần sau cùng nhau trên một trang:
| Phần của gói | Yêu cầu | Cách kết nối |
|---|---|---|
Tài nguyên Viewer, mount, và objViewer | Bắt buộc | Bộ render tài liệu cốt lõi |
| Thanh công cụ Viewer | Bắt buộc trong cấu trúc tham chiếu | Markup host; các nút gọi cùng objViewer |
| Ribbon tìm kiếm | Tùy chọn, mô-đun có giấy phép | doconutSearchBar(...).attach(objViewer) |
| Ribbon chú thích | Tùy chọn, mô-đun có giấy phép | doconutAnnotationBar(...).attach(objViewer) |
Mặc dù thanh công cụ Viewer chính là markup 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ữ 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:
<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 host để hỗ trợ xoay, thumbnail, in, toàn màn hình, bố cục và các trợ giúp trạng thái nút. Tải tệp host 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:
- Phát ra tài nguyên Viewer, Search và Annotation cùng nhau.
- Render thanh công cụ Viewer, các mount Ribbon và mount Viewer cùng nhau.
- Khởi tạo
docViewertrước tiên. - Tạo mỗi Ribbon có giấy phép và gắn nó vào cùng một
objViewer. - Mở tài liệu và giữ token của nó cho các yêu cầu mô-đun.
Doconut.TestApp.Distributed giữ nguyên cấu trúc UI này và cùng một trợ giúp Viewer-toolbar. Giá trị yêu cầu access bổ sung và các cài đặt retry render bất đồng bộ thuộc về truyền tải phân tán; chúng không thay đổi cách Viewer, thanh công cụ hoặ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, do đó hàm plugin jQuery của nó không tồn tại.
<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 nội dung, điều khiển kiểu dáng, hành động lưu và các hành động xuất/ảnh tùy chọn. Các thanh cung cấp các phương thức 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 host tùy chọn và các endpoint xuất/ảnh của Annotation để giữ khởi động tối thiểu. Xem Tìm kiếm và Chú thích để biết cấu hình đầy đủ cho từng tính năng, hoặc Giao diện tùy chỉnh để tạo kiểu hoặc thay thế thanh công cụ Viewer do host sở hữu.
Mở tài liệu
Phía máy chủ có một endpoint: dịch vụ Viewer được tiêm vào mở tài liệu và trả về một token phiên.
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ữ nó.
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { token });
});Client lấy token đó và chuyển nó cho widget bằng objViewer.View(token):
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 quy trình làm việc do máy chủ điều khiển, viewer.CloseDocument(token) ngay lập tức xóa phiên được lưu trong bộ nhớ cache, giải phóng engine render, xóa dấu bảo mật của nó và thu hồi token. Thời gian hết hạn trượt cuối cùng sẽ thực hiện cùng một quá trình dọn dẹp, 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à:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentXử lý token như một chứng chỉ bearer: không bao giờ ghi log nó, không bao giờ lưu trữ nó, 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 một 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 bảng thu nhỏ ở phía trái. Nếu không, xem Khắc phục sự cố.
Những gì bạn nhận được khi không có giấy phép
Thiếu giấy phép sẽ không gây lỗi. Viewer vẫn render bình thường, nhưng mỗi trang sẽ có watermark đá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ó được tìm thấy.
Trang này có hữu ích không?