Γρήγορη Εκκίνηση
Αποδώστε το πρώτο σας έγγραφο σε λίγα λεπτά
Αυτός ο οδηγός παίρνει μια εφαρμογή ASP.NET Core από ένα κενό Program.cs σε ένα έγγραφο που αποδίδεται στον περιηγητή: εγγραφή διακομιστή, το πλήρες πακέτο Viewer (γραμμή εργαλείων Viewer, τοποθέτηση Viewer και προαιρετικές ταινίες Αναζήτησης/Σχολιασμού), αναφορές πόρων, αρχικοποίηση πελάτη, άνοιγμα εγγράφου και εκτέλεση.
Ρύθμιση διακομιστή
AddDoconut() καταχωρεί τις υπηρεσίες· UseDoconutResources() και UseDoconut() συνδέουν το middleware. Η κλήση πόρων πρέπει να γίνει πρώτη. Οι κλήσεις συνεδρίας απαιτούνται επίσης — η προεπιλεγμένη ασφάλεια εγγράφων του Doconut επαληθεύει κάθε αίτημα σελίδας έναντι της κατάστασης συνεδρίας ASP.NET. Η κατάσταση συνεδρίας χρειάζεται μια κρυφή μνήμη: AddDistributedMemoryCache() παρέχει μία. Μια εφαρμογή MVC ή Razor Pages την καταχωρεί ήδη, αλλά μια εφαρμογή minimal-API δεν το κάνει, και χωρίς αυτή η εφαρμογή σταματά κατά την εκκίνηση με Unable to resolve service for type 'IDistributedCache'. Έχετε ήδη καταχωρήσει το Doconut κατά τη διάρκεια της Εγκατάσταση; Προχωρήστε στην επόμενη ενότητα.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // Η κατάσταση συνεδρίας χρειάζεται μια κρυφή μνήμη (MVC/Razor Pages προσθέτει ήδη μία)
builder.Services.AddSession(); // Η ασφάλεια εγγράφου Doconut βασίζεται στην κατάσταση συνεδρίας του ASP.NET
app.UseSession(); // Καλέστε το UseSession() πριν από το UseDoconut()
app.UseDoconutResources(); // Πρέπει να εγγραφεί πριν από το UseDoconut()
app.UseDoconut();Για μια διάταξη διαδρομών τύπου παραγωγής, αντιστοιχίστε το middleware εγγράφου σε έναν σαφή κλάδο και διατηρήστε τις τέσσερις ρυθμίσεις διαδρομής ευθυγραμμισμένες:
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 είναι μια τιμή συντονισμού· δεν αντιστοιχίζει έναν κλάδο ASP.NET Core από μόνη της. Σε αυτό το παράδειγμα ο κεντρικός υπολογιστής αντιστοιχίζει /doconut, έτσι ο πελάτης χρησιμοποιεί BasePath: 'doconut'. ResourcesPath εξυπηρετεί το ενσωματωμένο πακέτο στο /doconut-res, και η διαδρομή πόρων εικόνας του widget είναι επομένως ResPath: 'doconut-res/images'. Γράψτε και τα δύο χωρίς αρχικό κάθετο: ο viewer προσθέτει ένα μόνο του, έτσι το '/doconut' θα γίνει //doconut/DocImage.axd, το οποίο ο περιηγητής διαβάζει ως αίτημα σε έναν κεντρικό υπολογιστή με όνομα doconut, και ο viewer αποτυγχάνει με το "The viewer handler could not be reached".
Προσθήκη του viewer σε μια σελίδα
Ο Viewer είναι ο απαιτούμενος πυρήνας της σελίδας. Η επιφάνεια απόδοσής του χρησιμοποιεί δύο ενσωματωμένα divs:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Αντιμετωπίστε τη γραμμή εργαλείων, τις τοποθετήσεις μονάδων και την επιφάνεια Viewer ως μία σύνθεση σελίδας. Η Αναζήτηση και η Σχολιασμός ενσωματώνουν τις ενσωματωμένες ταινίες τους σε προαιρετικές τοποθετήσεις, αλλά αυτές οι μονάδες δεν είναι ποτέ αυτόνομες: συνδέονται πάντα με τον Viewer στην ίδια σελίδα. Χρησιμοποιήστε την ίδια σειρά όπως τα Doconut.TestApp και Doconut.TestApp.Distributed:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Κουμπιά πλοήγησης, ζουμ, αναζήτησης και σχολιασμού του Viewer -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Αναφορά των πόρων του viewer
Σε μια Razor view, η ενσωματωμένη υπηρεσία Viewer εκδίδει τις ετικέτες <link> και <script> του viewer με σειρά εξάρτησης — το widget είναι ένα πρόσθετο jQuery, οπότε το jQuery πρέπει να φορτωθεί πριν από τα σενάρια του 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
}))Για το πλήρες πακέτο Viewer, ζητήστε μαζί τους πόρους Viewer και μονάδας:
@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 είναι οι υποχρεωτικές σημαίες πυρήνα. Ποτέ μην δημοσιεύετε ένα παράδειγμα Search ή Annotation Ribbon χωρίς αυτές, την τοποθέτηση Viewer, και μια docViewer παρουσία. ReferenceCss και ReferenceScripts παραλείπουν τους πόρους μιας προαιρετικής μονάδας όταν η τρέχουσα άδεια δεν παρέχει αυτή τη δυνατότητα· ο πυρήνας Viewer εξακολουθεί να ξεκινά.
Αρχικοποίηση του προβολέα
Το widget στην πλευρά του πελάτη είναι ένα πρόσθετο jQuery. Αυτό είναι ένα ελάχιστο σύνολο πραγματικών επιλογών εκκίνησης (όχι ψευδοκώδικας):
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 () {
// οι σελίδες είναι ορατές; ασφαλές να κρύψετε έναν δείκτη φόρτωσης εδώ
},
// Προωθήστε τα γεγονότα κύκλου ζωής της σημείωσης στο ενσωματωμένο ribbon όταν είναι παρόν.
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. Δεν υπάρχει συνεπής κανόνας· αν κάνετε λάθος την κεφαλαιοποίηση, η επιλογή αγνοείται σιωπηρά (το widget επιστρέφει στην προεπιλογή του αντί να πετάξει σφάλμα).
Συναρμολόγηση του πλήρους πακέτου Viewer
Και οι δύο εφαρμογές αναφοράς .NET 6 εγκαθιστούν τα παρακάτω μέρη μαζί σε μία σελίδα:
| Μέρος του πακέτου | Απαίτηση | Πώς συνδέεται |
|---|---|---|
Πόροι Viewer, προσάρτηση και objViewer | Απαιτείται | Κύριος αποδοχέας εγγράφων |
| Γραμμή εργαλείων Viewer | Απαιτείται στη σύνθεση αναφοράς | Markup κεντρικού host· τα κουμπιά καλούν το ίδιο objViewer |
| Ribbon αναζήτησης | Προαιρετικό, άδεια μονάδα | doconutSearchBar(...).attach(objViewer) |
| Ribbon σχολιασμού | Προαιρετικό, άδεια μονάδα | doconutAnnotationBar(...).attach(objViewer) |
Παρόλο που η κύρια γραμμή εργαλείων Viewer είναι markup του host, εγκαθίσταται μαζί με το Viewer και δεν πρέπει ποτέ να τεκμηριώνεται ως ανεξάρτητος έλεγχος. Αυτό διατηρεί τη διάταξη, τις ετικέτες, τα εικονίδια και τους κανόνες εξουσιοδότησης υπό τον έλεγχο της εφαρμογής σας, ενώ κάθε κουμπί οδηγεί στην ίδια παρουσία 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>Η πλήρης γραμμή εργαλείων αναφοράς αντιγράφει επίσης το wwwroot/js/viewerToolbar.js στην εφαρμογή host για περιστροφή, μικρογραφία, εκτύπωση, πλήρη οθόνη, διάταξη και βοηθούς κατάστασης κουμπιών. Φορτώστε αυτό το αρχείο host μετά το Viewer.ReferenceScripts(...). Κρατήστε τον βοηθό και το <nav id="toolbar"> markup του μαζί όταν αντιγράφετε την πλήρη υλοποίηση demo.
Διατηρήστε τη σειρά αρχικοποίησης του πακέτου που χρησιμοποιούν και οι δύο εφαρμογές αναφοράς:
- Εκπομπή CSS για το Viewer και τις αδειοδοτημένες μονάδες.
- Απόδοση της γραμμής εργαλείων Viewer, των προσθηκών Search/Annotation και της προσθήκης Viewer μαζί.
- Εκπομπή scripts για το Viewer και τις αδειοδοτημένες μονάδες.
- Φορτώστε το
viewerToolbar.jsτης εφαρμογής host. - Αρχικοποιήστε το
docViewerκαι διατηρήστε το προκύπτονobjViewer. - Αρχικοποιήστε κάθε αδειοδοτημένο Ribbon Αναζήτησης ή Σχολιασμού.
- Καλέστε το
attach(objViewer)σε κάθε Ribbon. - Ανοίξτε το έγγραφο και διατηρήστε το token του για αιτήματα Viewer και μονάδων.
Doconut.TestApp.Distributed διατηρεί αυτήν την ακριβή σύνθεση UI και τον ίδιο βοηθό γραμμής εργαλείων Viewer. Η πρόσθετη τιμή αίτησης access και οι ρυθμίσεις επανάληψης ασύγχρονης απόδοσης ανήκουν στη διανεμημένη μεταφορά· δεν αλλάζουν τον τρόπο με τον οποίο συναρμολογούνται το Viewer, η γραμμή εργαλείων ή τα Ribbons.
Οι προστασίες στην πλευρά του διακομιστή είναι σημαντικές: όταν μια προαιρετική δυνατότητα δεν είναι διαθέσιμη, το script της δεν εκπομπείται, έτσι η λειτουργία plug-in του jQuery δεν υπάρχει.
<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 Ribbon. Η Αναζήτηση περιλαμβάνει ομάδες Find, Options και Results. Η Σχολιασμός περιλαμβάνει τα εργαλεία συγγραφής, τους ελέγχους στυλ, τις ενέργειες αποθήκευσης και προαιρετικές ενέργειες εξαγωγής/εικόνας. Οι γραμμές εκθέτουν τα open(), close(), reset() και isOpen()· πάντα καλέστε το attach(objViewer) μία φορά μετά τη δημιουργία τους.
Το παραπάνω παράδειγμα παραλείπει προαιρετικές κλήσεις επιστροφής host και τα σημεία άκρης εξαγωγής/εικόνας Σχολιασμού για να διατηρήσει την εκκίνηση ελάχιστη. Δείτε το Αναζήτηση και τις Σχολιασμούς για τη πλήρη ρύθμιση ανά χαρακτηριστικό, ή τα Προσαρμοσμένα Θέματα για να μορφοποιήσετε ή να αντικαταστήσετε τη γραμμή εργαλείων Viewer που ανήκει στο host.
Άνοιγμα εγγράφου
Η πλευρά του διακομιστή είναι ένα σημείο άκρης: η ενσωματωμένη υπηρεσία Viewer ανοίγει το έγγραφο και επιστρέφει ένα διακριτικό συνεδρίας.
app.MapPost("/api/open", async (Viewer viewer) =>
{
// Το token είναι αδιαφανές — δώστε το στο widget, ποτέ μην το καταγράψετε ή το αποθηκεύσετε.
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { token });
});Ο πελάτης ανακτά αυτό το διακριτικό και το παραδίδει στο widget με objViewer.View(token):
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});Κλείσιμο του εγγράφου
Καλέστε objViewer.Close() όταν ο χρήστης αφήνει το viewer ή ανοίγει ένα αντικαταστατικό έγγραφο. Σε ροές εργασίας που ελέγχονται από τον διακομιστή, viewer.CloseDocument(token) αφαιρεί αμέσως τη cached συνεδρία, απελευθερώνει τη μηχανή απόδοσης, διαγράφει το σήμα ασφαλείας της και ανακαλεί το διακριτικό. Η κυλιόμενη λήξη τελικά εκτελεί την ίδια εκκαθάριση, αλλά συνιστάται το ρητό κλείσιμο για μεγάλα έγγραφα.
Η ολοκληρωμένη ροή αιτήματος είναι:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentΑντιμετωπίστε το διακριτικό σαν διαπιστευτήριο τύπου bearer: μην το καταγράφετε ποτέ, μην το αποθηκεύετε ποτέ, παραχωρήστε το μόνο στο widget. Αναγνωρίζει μια ζωντανή συνεδρία εγγράφου στον διακομιστή και σταματά να λειτουργεί όταν η συνεδρία αυτή λήξει — ανοίξτε ξανά το έγγραφο για να λάβετε ένα νέο.
Εκτέλεση
Τοποθετήστε ένα PDF στο wwwroot/files/Sample.pdf, εκτελέστε το dotnet run και ανοίξτε τη σελίδα που φιλοξενεί το widget. Η πρώτη σελίδα αποδίδεται στο viewer, με ένα πάνελ μικρογραφιών στα αριστερά. Αν δεν συμβαίνει, δείτε Αντιμετώπιση προβλημάτων.
Τι λαμβάνετε χωρίς άδεια
Η έλλειψη άδειας δεν προκαλεί σφάλμα. Το viewer αποδίδει κανονικά, αλλά κάθε σελίδα φέρει ένα υδατογράφημα αξιολόγησης. Δείτε Ρύθμιση άδειας για το πώς η Doconut εντοπίζει μια άδεια και τι αλλάζει όταν το κάνει.
Ήταν αυτή η σελίδα χρήσιμη;