クイックスタート
数分で最初のドキュメントをレンダリング
このハンドブックは、空の Program.cs からブラウザでレンダリングされたドキュメントまで、ASP.NET Core アプリを段階的に構築します:サーバー登録、完全な Viewer パッケージ(Viewer ツールバー、Viewer マウント、オプションの検索/注釈リボン)、アセット参照、クライアント初期化、ドキュメントのオープン、そして実行です。
ホスト アプリケーションは netstandard2.1 をサポートするランタイム、すなわち .NET Core 3.0 以降を対象とする必要があります。このリリースに同梱されたリファレンス アプリケーションは net8.0 を対象としています。
サーバー設定
AddDoconut() はサービスを登録し、UseDoconutResources() と UseDoconut() がミドルウェアを接続します。リソース呼び出しは最初に行う必要があります。セッション呼び出しも必須です — Doconut のデフォルト ドキュメント セキュリティはすべてのページ要求を ASP.NET セッション状態と照合します。セッション状態にはキャッシュが必要です:AddDistributedMemoryCache() が提供します。MVC または Razor Pages アプリはすでに登録していますが、最小 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(); // UseDoconut() の前に UseSession() を呼び出します
app.UseDoconutResources(); // UseDoconut() の前に登録する必要があります
app.UseDoconut();リファレンス アプリケーションは従来の DocImage.axd リクエスト形状を保持し、ドキュメント ミドルウェアを一致するブランチにマウントします。これは既存の .NET Standard アプリケーションを引き継ぐ際にコピーすべき構成です。クライアントは常に同じ URL を呼び出し続けます。
builder.Services.AddDoconut(options =>
{
options.UnsafeMode = false; // Doconut のセッションチェックを有効に保ちます
options.ShowDoconutInfo = false;
});
builder.Services.AddDistributedMemoryCache(); // セッション状態にはキャッシュが必要です(MVC/Razor Pages はすでに追加されています)
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(3600);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// 一部のドキュメント形式でのマルチバイトエンコーディングに必要です。
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseDoconutResources(); // ドキュメント ブランチの前
app.MapWhen(
context => context.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
branch => branch.UseDoconut());UseDoconut() はこのリリースではオプションを受け取りません — すべて AddDoconut() に移行しました。MiddlewarePath は調整用の値であり、単独で ASP.NET Core のブランチをマッピングするものではありません。ResourcesPath は /doconut-res に埋め込まれたバンドルを提供し、ウィジェットの画像リソースパスはそのため ResPath: 'doconut-res/images' となります。
ページにビューアを追加
Viewer はページに必須のコアです。そのレンダリング領域は 2 つの入れ子になった div を使用します:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>ツールバー、モジュールマウント、Viewer の領域を 1 つのページ構成として扱います。検索と注釈はそれぞれの埋め込みリボンをオプションのマウントに注入しますが、これらのモジュールは単体で使用されることはなく、常に同じページの Viewer に結合されます。
<nav id="toolbar" aria-label="Document viewer controls">
<!-- ビューアのナビゲーション、ズーム、検索、注釈ボタン -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>ビューア資産の参照
Razor ビューでは、注入された Viewer サービスがビューアの <link> と <script> タグを依存関係の順序で出力します。ウィジェットは jQuery プラグインなので、ビューアのスクリプトよりも先に jQuery を読み込む必要があります。
@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 は必須のコアフラグです。これらと Viewer のマウント、docViewer インスタンスがなければ、Search や Annotation リボンの例を公開しないでください。ReferenceCss と ReferenceScripts は、現在のライセンスがその機能を許可していない場合にオプションモジュールのリソースを省略しますが、コア Viewer は起動します。
これらの呼び出しは、手作業でコピーしたスクリプトファイルに取って代わります。アプリケーションがまだ独自の docViewer.js、documentLinks.js、または docViewer.UI.js を読み込んでいる場合は、ページが出力されたリソースで正しく動作したことを確認したらそれらのタグを削除してください — 詳細は マイグレーション ガイド を参照してください。
ビューアの初期化
クライアント側ウィジェットは jQuery プラグインです。以下は実際の初期化オプションの最小セットです(疑似コードではありません)。
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 () {
// 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);
}
});このページがまったく機能するかどうかは、次の 2 点で決まります。
- コールバックはオプションです。
onViewerReady、onViewerBusy、onCopy、onAutoLoadStatusはオプションオブジェクトに渡します。従来の Viewer はコンテナ ID からグローバル関数名を導出して呼び出していましたが、こちらはそうではありません。グローバル関数としてだけ宣言したハンドラは実行されず、何も報告されません。 ResPathはリソースのプレフィックスを指します。 アプリケーションルートではなく、デフォルトのResourcesPathはdoconut-res/imagesです。ResourcesPathを変更した場合は、独自のプレフィックスを使用してください。
これらのどちらもサイレントに失敗します — 詳細は トラブルシューティング と マイグレーション ガイド を参照してください。
オプション名の大小文字は実際に混在しています。showThumbs、autoLoad、pageZoom は camelCase、FitType、BasePath、ResPath は PascalCase です。統一された規則はなく、大小文字を間違えるとオプションはサイレントに無視され(ウィジェットはデフォルトにフォールバックします)ます。
完全な Viewer パッケージの構成
両方のリファレンスアプリケーションは、次のパーツを 1 ページにまとめてインストールします。
| パッケージの構成要素 | 必要性 | 接続方法 |
|---|---|---|
Viewer リソース、マウント、objViewer | 必須 | コア文書レンダラ |
| Viewer ツールバー | 参照構成で必須 | ホストマークアップ;ボタンは同じ objViewer を呼び出す |
| Search リボン | オプション(ライセンス対象モジュール) | doconutSearchBar(...).attach(objViewer) |
| Annotation リボン | オプション(ライセンス対象モジュール) | doconutAnnotationBar(...).attach(objViewer) |
メインの Viewer ツールバーはホストマークアップですが、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>両方のリファレンスアプリケーションで使用されているパッケージ初期化順序を保ってください。
- Viewer とライセンスモジュール用の CSS を出力する。
- Viewer ツールバー、Search/Annotation のマウント、Viewer のマウントを同時に描画する。
- Viewer とライセンスモジュール用のスクリプトを出力する。
- ホストアプリケーション独自のツールバーヘルパースクリプトを読み込む。
docViewerを初期化し、生成されたobjViewerを保持する。- 各ライセンス対象の Search または Annotation リボンを初期化する。
- すべてのリボンに対して
attach(objViewer)を呼び出す。 - 文書を開き、Viewer とモジュールのリクエストに使用するトークンを保持する。
サーバー側のガードは重要です。オプション機能が利用できない場合、そのスクリプトは出力されず、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 を生成します。Search には「検索」「オプション」「結果」グループが、Annotation には著者ツール、スタイルコントロール、保存アクション、オプションのエクスポート/画像アクションが含まれます。バーは open()、close()、reset()、isOpen() を公開します。作成直後に必ず attach(objViewer) を一度呼び出してください。
上記の例は、起動を最小限に抑えるためにオプションのホストコールバックや Annotation のエクスポート/画像エンドポイントを省略しています。完全な機能別セットアップは 検索 と 注釈 を、ホスト所有の Viewer ツールバーのスタイル変更や置き換えは カスタムテーマ を参照してください。
ドキュメントを開く
サーバー側は 1 つのエンドポイントです。注入された Viewer サービスがドキュメントを開き、セッショントークンを返します。このリリースではオープンは非同期です。
app.MapPost("/api/open", async (Viewer viewer) =>
{
// トークンは不透明です — ウィジェットに渡すだけにし、決してログに記録したり永続化したりしないでください。
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { token });
});クライアントはそのトークンを取得し、objViewer.View(token) でウィジェットに渡します:
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});最初のオープン前にウォームアップする必要はありません。アプリケーションを引き継ぐ場合は、InitCache() の呼び出しとそれを保護しているフラグを削除してください — そのメソッドはもはや存在しません。
ドキュメントを閉じる
objViewer.Close() を、ユーザーがビューアーを離れるか代替ドキュメントを開くときに呼び出します。サーバー主導のワークフローでは、viewer.CloseDocument(token) がキャッシュされたセッションを即座に削除し、レンダリングエンジンを破棄し、セキュリティマーカーを削除し、トークンを無効化します。スライディング有効期限でも最終的に同様のクリーンアップが行われますが、大きなドキュメントの場合は明示的に閉じることが推奨されます。
完了したリクエストフローは次のとおりです:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentトークンはベアラークレデンシャルとして扱い、決してログに記録したり永続化したりせず、ウィジェットにのみ渡してください。トークンはサーバー上のライブドキュメントセッションを識別し、セッションが期限切れになると機能しなくなります — 新しいトークンを取得するにはドキュメントを再度開いてください。
実行する
wwwroot/files/Sample.pdf に PDF を配置し、dotnet run を実行してウィジェットをホストするページを開きます。最初のページがビューアーに表示され、左側にサムネイルパネルが表示されます。表示されない場合は、トラブルシューティング を参照してください。
ライセンスなしで得られるもの
ライセンスがない場合でも例外は発生しません。ビューアーは通常通りレンダリングしますが、すべてのページに評価用の透かしが付加されます。Doconut がライセンスを取得する方法と取得後の変更点については、ライセンス設定 を参照してください。
このページは役に立ちましたか?