빠른 시작

MVC 5 또는 Web Forms에서 첫 번째 문서를 렌더링하기

이 안내서는 .NET Framework 4.7.2 기반 ASP.NET 애플리케이션을 설치된 패키지에서 브라우저에 렌더링되는 문서로 전환하는 과정을 단계별로 보여줍니다. 여기에는 Global.asax, 문서를 여는 엔드포인트, 뷰어 자산, 위젯, 그리고 이들을 연결하는 토큰이 포함됩니다. Web.config에 설치에서 설명한 등록이 되어 있다고 가정합니다 — DocImage.axd 핸들러와 리소스 모듈이 없으면 아래 내용이 서버에 도달할 수 없습니다.

Global.asax에서 Doconut 구성

MVC 5 애플리케이션에 대한 전체 Application_Start 구현:

csharp
// Global.asax.cs - compose once per worker process, release on shutdown.
protected void Application_Start()
{
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

    Doconut.DoconutHost.Initialize(options =>
    {
        options.LicensePath = HostingEnvironment.MapPath("~/wwwroot/Doconut.Viewer.lic");
        options.UnsafeMode  = false; // a page is served only to the session that opened the document
    });

    // MVC only: keep the two Doconut endpoints out of routing.
    RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    RouteTable.Routes.IgnoreRoute("doconut-res/{*pathInfo}");
}

protected void Application_End()
{
    Doconut.DoconutHost.Shutdown();
}
  • **Encoding.RegisterProvider**는 일부 문서 형식에서 사용하는 레거시 코드 페이지를 프로세스에서 사용할 수 있게 합니다. 첫 번째 문서를 열기 전에 한 번만 호출하면 됩니다.
  • **UnsafeMode = false**는 기본값이며, 실수로 변경되지 않도록 명시적으로 적어 둔 것입니다. DocImage.axd는 문서를 연 ASP.NET 세션에만 페이지를 제공합니다.
  • 두 개의 IgnoreRoute 호출은 MVC 라우팅이 DocImage.axd와 /doconut-res/* 경로로 향하지 않도록 합니다. 이 경로들은 핸들러와 모듈이 제공하므로, Web Forms 애플리케이션에서는 해당 라인들이 필요하지 않습니다.
  • **DoconutHost.Shutdown**은 애플리케이션 풀 재시작 시 Doconut의 백그라운드 작업을 중지하고 서비스를 해제합니다. Initialize가 실행되지 않았더라도 안전하게 호출할 수 있습니다.

문서 열기

문서를 열면 불투명 토큰이 반환됩니다. 위젯은 각 페이지 요청마다 해당 토큰을 전송합니다.

MVC 5에서는 컨트롤러 액션이 DoconutHost.Services에서 Viewer 서비스를 가져옵니다 — MVC 5는 생성자 주입을 지원하지 않기 때문입니다:

csharp
// POST /Document/Open
[HttpPost]
public async Task<ActionResult> Open()
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();

    // The token is opaque - hand it to the viewer, never log or persist it.
    string token = await viewer.OpenDocumentAsync(Server.MapPath("~/files/Sample.pdf"));
    return Json(new { token });
}

문서는 세션 상태가 있는 요청 내부에서 열려야 합니다. 문서를 열어야 현재 ASP.NET 세션이 해당 페이지에 접근할 수 있기 때문입니다. MVC 컨트롤러와 Web Forms 페이지는 기본적으로 세션 상태를 가집니다. System.Web.SessionState.IRequiresSessionState를 구현하지 않은 사용자 정의 IHttpHandler는 세션을 갖지 않으므로, 이 핸들러에서 문서를 열면 뷰어는 모든 페이지에서 “You Are Not Authorized To View This Page.” 라는 메시지를 표시합니다.

Web Forms에서는 페이지 자체가 코드 비하인드에서 문서를 열 수 있습니다. OpenDocument는 OpenDocumentAsync의 동기식 형태이며, 비동기 페이지를 사용하지 않는 코드 비하인드용으로 설계되었습니다:

csharp
// Default.aspx.cs
protected string Token;

protected void Page_Load(object sender, EventArgs e)
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
    Token = viewer.OpenDocument(Server.MapPath("~/files/Sample.pdf"));
}

토큰을 인증 자격 증명처럼 취급하십시오: 절대 로그에 남기거나 영구 저장하지 말고, 위젯에만 전달하십시오. 문서 세션이 만료되면 토큰은 더 이상 작동하지 않으므로, 새 토큰을 얻기 위해 문서를 다시 열어야 합니다.

뷰어 자산 참조

Viewer.ReferenceCss 및 Viewer.ReferenceScripts는 임베드된 자산에 대한 <link>와 <script> 태그를 종속 순서대로 출력하며, /doconut-res/를 가리킵니다. 위젯은 jQuery 플러그인이므로 jQuery가 먼저 로드됩니다.

MVC 5. 뷰에는 @inject가 없으므로 컨트롤러에서 태그를 생성하고 ViewBag에 전달합니다:

csharp
public ActionResult Index()
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
    ViewBag.ViewerCss = viewer.ReferenceCss(new CssConfig
    {
        IncludeBootstrapCss = true,
        IncludeViewerCss    = true
    });
    ViewBag.ViewerScripts = viewer.ReferenceScripts(new ScriptConfig
    {
        IncludeJQuery        = true,
        IncludeBootstrap     = true,
        IncludeViewerScripts = true
    });
    return View();
}
html
@Html.Raw(ViewBag.ViewerCss)
...
@Html.Raw(ViewBag.ViewerScripts)

이 작업은 컨트롤러에 두세요. MVC 5는 뷰를 처음 제공할 때 컴파일하며, 추가 컴파일러 패키지가 없으면 해당 컴파일러는 C# 5만 이해합니다 — ?.도 없고, $"..."도 없으며, nameof도 없습니다. 이러한 구문을 사용하는 뷰는 Visual Studio에서 정상적으로 컴파일되지만 브라우저에서는 빈 500 오류가 반환됩니다.

Web Forms. 코드 비하인드에서 동일한 문자열을 노출하고 <%= %>를 사용해 출력합니다:

html
<head>
    <%= ViewerCss %>
</head>
<body>
    ...
    <%= ViewerScripts %>
</body>

IncludeViewerCss와 IncludeViewerScripts는 필수 핵심입니다. 검색 및 주석 리본을 추가하려면 IncludeSearchCss, IncludeAnnotationCss, IncludeSearchScripts, IncludeSearchBar, IncludeAnnotationScripts 및 IncludeAnnotationBar도 설정합니다. 라이선스가 해당 기능을 허용하지 않을 경우 두 방법 모두 해당 리본 파일을 제외하지만, 뷰어 자체는 여전히 시작됩니다.

페이지에 뷰어를 추가

렌더링 영역은 두 개의 중첩된 div입니다:

html
<div id="divDocViewer">
    <div id="div_ctlDoc"></div>
</div>

외부 div에 실제 높이를 지정하세요 — 뷰어는 시작 시 컨테이너를 채우고 그 크기를 측정합니다. 뷰어는 자체 툴바를 그리지 않으며, GotoPage, Next, Zoom, FitType 등은 페이지에서 자체 버튼에 연결하는 메서드이며, 뷰어 옆에 배치됩니다:

html
<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.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>
</nav>

뷰어를 초기화하고 문서를 표시

뷰어 스크립트를 로드한 후 위젯을 생성하고 토큰을 전달합니다:

javascript
var objViewer = $('#div_ctlDoc').docViewer({
    showThumbs: true,
    autoLoad:   false,
    pageZoom:   100,
    FitType:    'width',
    BasePath:   '/',                   // '/' means DocImage.axd at the site root
    ResPath:    'doconut-res/images',  // no leading '/'
    onViewerReady: function () {
        // pages are visible; hide a loading indicator here
    },
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

fetch('/Document/Open', { method: 'POST' })
    .then(function (resp) { return resp.json(); })
    .then(function (data) { objViewer.View(data.token); });

Web Forms에서는 페이지가 직접 문서를 열기 때문에 마지막 세 줄을 objViewer.View('<%= Token %>'); 로 바꿉니다.

  • BasePath: '/' 은 사이트 루트의 DocImage.axd를 가리킵니다. 가상 디렉터리에서 애플리케이션을 사용할 경우 슬래시 없이 이름만 사용하세요 ('myapp'은 /myapp/DocImage.axd를 요청합니다). 앞에 슬래시가 붙으면 ('/myapp') //myapp/DocImage.axd가 되어 브라우저가 다른 호스트에 대한 요청으로 해석합니다.
  • ResPath: 'doconut-res/images' — 아이콘은 리소스 루트 아래 한 단계 폴더에 있습니다. 'doconut-res'만 지정하면 모든 아이콘이 404를 반환하여 툴바 아이콘과 로딩 스피너가 표시되지 않지만 문서는 여전히 렌더링됩니다. 앞에 슬래시를 붙이면 BasePath와 동일하게 깨집니다.
  • 콜백은 옵션입니다. onViewerReady, onError 등은 docViewer(...)에 전달되는 객체 안에 포함됩니다. ctlDoc_ViewerReady와 같이 뷰어 이름을 딴 전역 함수는 호출되지 않습니다.
  • 뷰어를 유지한 뒤 View를 별도로 호출하세요. docViewer(...)는 Zoom, Next, FitType 등을 가진 객체를 반환하고, View(...)는 다른 것을 반환합니다. var objViewer = $('#div_ctlDoc').docViewer({...}).View(token)와 같이 사용하면 문서는 렌더링되지만 첫 번째 툴바 클릭 시 objViewer.Zoom is not a function 오류가 발생합니다.

옵션 이름은 camelCase(showThumbs, autoLoad, pageZoom)와 PascalCase(FitType, BasePath, ResPath)가 혼합되어 있습니다. 오타가 있는 옵션은 무시되고 위젯은 기본값을 사용합니다.

한 줄 대안

문서만 표시하면 되고 자체 툴바가 필요 없는 페이지의 경우, Viewer.RenderViewer가 전체 초기화 스크립트를 작성합니다 — docViewer(...)에 BasePath: '/'와 ResPath: 'doconut-res/images'를 지정하고, 이어서 .View(token)을 호출합니다. 이 기능은 이 패키지에만 존재합니다:

csharp
// Web Forms code-behind, after opening the document
ViewerInit = viewer.RenderViewer("div_ctlDoc", Token);
html
<div id="div_ctlDoc" style="height: 100vh"></div>
<%= ViewerScripts %>
<%= ViewerInit %>

줌, 맞춤, 썸네일 패널을 변경하려면 세 번째 인수로 ViewerConfig를 전달하십시오. 생성된 스크립트가 뷰어에 대한 참조를 유지하지 않기 때문에, 페이지에 자체 툴바가 필요할 경우 위에 있는 수동 초기화를 사용하십시오.

문서 닫기

사용자가 뷰어를 떠나거나 교체 문서를 열 때 objViewer.Close(true)를 호출하면 위젯이 제거되고 DocImage.axd에 닫기 요청이 전송됩니다. 인수를 전달하지 않은 objViewer.Close()는 위젯만 제거합니다. 서버에서는 viewer.CloseDocument(token)이 문서 세션을 즉시 삭제하고 렌더링 리소스를 해제하며 해당 세션의 페이지 접근 권한을 회수합니다. 슬라이딩 만료도 결국 동일한 작업을 수행하지만, 큰 문서를 명시적으로 닫으면 메모리를 더 빨리 해제할 수 있습니다.

실행

~/files/Sample.pdf에 PDF 파일을 배치하고 IIS Express에서 사이트를 시작합니다(F5 in Visual Studio). 위젯을 호스팅하는 페이지를 열면 첫 페이지가 렌더링되고 왼쪽에 썸네일이 표시됩니다. 표시되지 않을 경우 문제 해결을 참고하십시오.

라이선스 없이 얻는 것

라이선스가 없다고 오류가 발생하지는 않습니다. 뷰어는 정상적으로 렌더링되지만 모든 페이지에 평가용 워터마크가 표시됩니다. Doconut이 라이선스를 찾는 위치에 대해서는 라이선스 설정을 확인하십시오.

이 페이지가 도움이 되었나요?