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, 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.
Ứng dụng host phải nhắm tới một runtime hỗ trợ netstandard2.1 — .NET Core 3.0 hoặc mới hơn. Các ứng dụng tham chiếu được phát hành cùng bản này nhắm tới net8.0.
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 thực hiện 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 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ý nó, nhưng một ứng dụng minimal-API thì không, và nếu không có nó ứng dụng sẽ dừng khi khởi động với Unable to resolve service for type 'IDistributedCache'. Đã đăng ký Doconut trong quá trình Cài đặt chưa? Bỏ qua 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 một)
builder.Services.AddSession(); // Bảo mật tài liệu Doconut dựa trên trạng thái session ASP.NET
app.UseSession(); // gọi UseSession() trước UseDoconut()
app.UseDoconutResources(); // phải được đăng ký trước UseDoconut()
app.UseDoconut();Ứng dụng tham chiếu giữ nguyên dạng yêu cầu lịch sử DocImage.axd và gắn middleware tài liệu trên một nhánh phù hợp. Đây là cấu hình cần sao chép khi bạn đang đưa một ứng dụng .NET Standard hiện có lên, vì client vẫn gọi cùng một URL như trước đây:
builder.Services.AddDoconut(options =>
{
options.UnsafeMode = false; // giữ kiểm tra session của Doconut's được bật
options.ShowDoconutInfo = false;
});
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(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(3600);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// Cần thiết cho các mã hoá đa byte trong một số định dạng tài liệu.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseDoconutResources(); // TRƯỚC nhánh tài liệu
app.MapWhen(
context => context.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
branch => branch.UseDoconut());UseDoconut() không nhận bất kỳ tùy chọn nào trong phiên bản này — mọi thứ đã chuyển sang AddDoconut(). MiddlewarePath là một giá trị phối hợp; nó không tự mình ánh xạ một nhánh ASP.NET Core. ResourcesPath phục vụ bundle 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'.
Thêm trình xem vào trang
Viewer là phần lõi bắt buộc của trang. Bề mặt render 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 module gắn 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 mount tùy chọn, nhưng các module đó không bao giờ độc lập: chúng luôn gắn vào Viewer trên cùng một trang.
aria-label="Điều khiển trình xem tài liệu"
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Các nút điều hướng, thu phóng, Tìm kiếm và Ghi chú của Viewer -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Tham chiếu các tài sản của trình xem
Trong một Razor view, dịch vụ Viewer được tiêm vào phát ra các thẻ <link> và <script> của trình xem 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 trình xem:
@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 tài nguyên Viewer và các module cùng nhau:
@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ụ Search hoặc Annotation Ribbon mà không có chúng, phần gắn Viewer, và một docViewer
instance. ReferenceCss và ReferenceScripts bỏ qua tài nguyên của module tùy chọn khi
giấy phép hiện tại không cấp quyền; core Viewer vẫn khởi động.
Các lời gọi này thay thế các tệp script được sao chép thủ công.
Nếu ứng dụng vẫn tải docViewer.js, documentLinks.js, hoặc docViewer.UI.js của riêng nó, hãy loại bỏ các thẻ đó một khi trang
hoạt động từ các tài nguyên được phát — xem hướng dẫn di chuyển.
Khởi tạo trình xem
Widget phía client 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):
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 () {
// các trang đã hiển thị; an toàn để ẩn loading spinner ở đây
},
// Chuyển tiếp các sự kiện vòng đời annotation 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);
}
});Hai chi tiết quyết định liệu trang này có hoạt động hay không:
- Callbacks là các tùy chọn.
onViewerReady,onViewerBusy,onCopy, và
onAutoLoadStatusđược truyền trong đối tượng tùy chọn. Trình xem trước đã suy ra tên hàm toàn cục từ id container và gọi chúng; phiên bản này không làm như vậy.
Một handler được khai báo chỉ là hàm toàn cục sẽ không bao giờ chạy, và không có gì được báo cáo. ResPathchỉ tới tiền tố tài nguyên, không phải gốc của ứng dụng. Với mặc định
ResourcesPath, giá trị đó làdoconut-res/images. Nếu bạn đã thay đổiResourcesPath, hãy sử dụng
tiền tố của riêng bạn thay thế.
Cả hai đều thất bại im lặng — xem Khắc phục sự cố và
hướng dẫn di chuyển.
Việc viết hoa chữ cái của 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 sẽ khiến tùy chọn bị bỏ qua im lặng (widget sẽ quay lại 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 đề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, phần gắn, và objViewer | Bắt buộc | Bộ kết xuất tài liệu lõi |
| Thanh công cụ Viewer | Bắt buộc trong cấu thành tham chiếu | Markup của host; các nút gọi cùng objViewer |
| Ribbon Tìm kiếm | Tùy chọn, module có giấy phép | doconutSearchBar(...).attach(objViewer) |
| Ribbon Ghi chú | Tùy chọn, module có giấy phép | doconutAnnotationBar(...).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ữ bố cục, nhãn, biểu tượng, và
các quy tắc ủy quyền dưới quyền kiểm soát của ứng dụng của bạn trong khi mỗi nút điều khiển cùng một
instance của 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>Giữ thứ tự khởi tạo gói được sử dụng bởi cả hai ứng dụng tham chiếu:
- Phát CSS cho Viewer và các module có giấy phép.
- Hiển thị thanh công cụ Viewer, các phần gắn Search/Annotation, và phần gắn Viewer cùng nhau.
- Phát script cho Viewer và các module có giấy phép.
- Tải script trợ giúp thanh công cụ riêng của ứng dụng host.
- Khởi tạo
docViewervà giữ lạiobjViewerkết quả. - Khởi tạo mỗi Ribbon Search hoặc Annotation có giấy phép.
- Gọi
attach(objViewer)trên mỗi Ribbon. - Mở tài liệu và giữ lại token của nó cho các yêu cầu Viewer và module.
Các bảo vệ phía server rất quan trọng: khi một khả năng tùy chọn không khả dụng,
script của nó không được phát, vì vậy 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 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ữ khởi động tối thiểu. Xem Tìm kiếm và
Ghi chú để có cài đặt đầy đủ 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 thuộc sở hữu của host.
Mở một tài liệu
Phía máy chủ là một điểm cuối: dịch vụ Viewer được tiêm vào mở tài liệu và trả về một token phiên. Việc mở là bất đồng bộ trong phiên bản này.
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 });
});Máy khách lấy token đó và truyề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);
});Không có gì cần khởi động trước lần mở đầu tiên. Nếu bạn đang chuyển tiếp một ứng dụng, hãy xóa mọi lời gọi InitCache() và cờ bảo vệ nó — phương thức này không còn tồn tại.
Đóng tài liệu
Gọi objViewer.Close() khi người dùng rời khỏi trình xem 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ớ đệm, giải phóng động cơ 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 một cách rõ ràng được khuyến nghị cho các tài liệu lớn.
Luồng yêu cầu đã hoàn thà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ỉ mang: không bao giờ ghi lại, không bao giờ lưu trữ, chỉ truyề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 tệp PDF tại wwwroot/files/Sample.pdf, chạy dotnet run, và mở trang chứa widget. Trang đầu tiên sẽ được hiển thị trong trình xem, 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 mà không có giấy phép
Một giấy phép thiếu sẽ không gây lỗi. Trình xem vẫn hiển thị bình thường, nhưng mỗi trang đều có dấu nước đánh giá. Xem Cài đặt giấy phép để biết cách Doconut tìm giấy phép và những gì sẽ thay đổi khi nó được tìm thấy.
Trang này có hữu ích không?