Bắt đầu nhanh
Hiển thị tài liệu đầu tiên của bạn trong vài phút
Bài 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, mount Viewer và các ruy-băng Tìm kiếm/Chú thích tùy chọn), tham chiếu tài nguyên, khởi tạo phía 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 tài nguyên phải được gọi trước. Các lệnh session cũng bắt buộc — bảo mật tài liệu Doconut mặc định xác thực mỗi yêu cầu trang dựa trên trạng thái session ASP.NET. Đã đăng ký Doconut trong Cài đặt chưa? Bỏ qua phần này và chuyển sang mục tiếp theo.
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();Đối với bố cục đường dẫn kiểu production, á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, "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 là một giá trị phối hợp; nó không tự động ánh xạ một nhánh ASP.NET Core. Trong ví dụ này máy chủ ánh xạ /doconut, vì vậy client phải dùng BasePath: '/doconut'. ResourcesPath phục vụ bundle nhúng tại /doconut-res, và đường dẫn tài nguyên ảnh của widget do đó là ResPath: '/doconut-res/images'.
Thêm viewer vào một trang
Viewer là thành 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 mount module và bề mặt Viewer như một bố cục trang duy nhất. Tìm kiếm và Chú thích chèn ruy-băng nhúng của chúng vào các mount 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">
<!-- 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:
@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 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ờ lõi bắt buộc. Không bao giờ công bố một ví dụ ruy-băng Tìm kiếm hoặc Chú thích mà không có chúng, mount Viewer, và một instance 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 lõi vẫn khởi động.
Khởi tạo viewer
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: '/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);
}
});Cách viết hoa 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 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 6 đề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 lõi |
| Thanh công cụ Viewer | Bắt buộc trong bố cục tham chiếu | Markup máy chủ; các nút gọi cùng một objViewer |
| Ruy-băng Tìm kiếm | Tùy chọn, mô-đun có giấy phép | doconutSearchBar(...).attach(objViewer) |
| Ruy-băng 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 máy chủ, 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à 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 instance 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 máy chủ để hỗ trợ quay, thumbnail, in, toàn màn hình, bố cục và các helper trạng thái nút. Tải file host này sau Viewer.ReferenceScripts(...). Giữ helper 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 CSS cho Viewer và các mô-đun có giấy phép.
- Render thanh công cụ Viewer, các mount Tìm kiếm/Chú thích và mount Viewer cùng nhau.
- Phát ra script cho Viewer và các mô-đun có giấy phép.
- Tải
viewerToolbar.jscủa ứng dụng host. - Khởi tạo
docViewervà giữ lạiobjViewerđã tạo. - Khởi tạo mỗi Ribbon Tìm kiếm hoặc Chú thích có giấy phép.
- Gọi
attach(objViewer)trên mọi Ribbon. - 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à helper 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 Ribbon được lắp ráp.
Các guard 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ó sẽ không được phát ra, vì vậy hàm plugin jQuery của nó sẽ 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 DOM Ribbon riêng. Tìm kiếm chứa các nhóm Find, Options và Results. Chú thích 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 host tùy chọn và các endpoint xuất/ảnh của Chú thích để giữ 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ở tài liệu
Phía server chỉ có một endpoint: dịch vụ Viewer được tiêm sẽ mở tài liệu và trả về một token session.
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 });
});Client sẽ lấy token đó và truyề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 các workflow do server điều khiển, viewer.CloseDocument(token) sẽ ngay lập tức xóa session đã 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 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 / CloseDocumentXem token như một chứng chỉ bearer: không bao giờ ghi log, không bao giờ lưu trữ, chỉ truyền cho widget. Nó xác định một session tài liệu đang hoạt động trên server và sẽ ngừng hoạt động khi session hết hạn — mở lại tài liệu để nhận token mới.
Chạy thử
Đặ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 panel thumbnail ở bên 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
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ó 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?