Free ASP.NET MVC File Manager with Built‑in PDF Viewer
← Back to Blog••6 min read

Free ASP.NET MVC File Manager with Built‑in PDF Viewer

Build a Free ASP.NET MVC File Manager that Shows PDFs

You want a page that lists the files stored on the server, lets the user download or delete them, and opens any PDF in‑browser without leaving the site. The following guide shows you how to do exactly that with plain ASP.NET MVC, the built‑in session cache, and Doconut’s universal document viewer.


1. Scaffold a Minimal MVC Application

Create a new MVC project in Visual Studio (any edition, including the free Community edition).

Add a FileController that will host the file‑manager actions:

public class FileController : Controller
{
    private readonly string _root = Path.Combine(Directory.GetCurrentDirectory(), "App_Data", "Docs");

    public IActionResult Index()
    {
        var files = Directory.GetFiles(_root).Select(Path.GetFileName).ToList();
        return View(files);
    }

    public IActionResult Download(string name)
    {
        var path = Path.Combine(_root, name);
        if (!System.IO.File.Exists(path)) return NotFound();
        var mime = "application/octet-stream";
        return PhysicalFile(path, mime, name);
    }

    public IActionResult Delete(string name)
    {
        var path = Path.Combine(_root, name);
        if (System.IO.File.Exists(path)) System.IO.File.Delete(path);
        return RedirectToAction(nameof(Index));
    }

    public async Task<IActionResult> ViewPdf(string name)
    {
        var path = Path.Combine(_root, name);
        if (!System.IO.File.Exists(path)) return NotFound();

        // The viewer token is created here (see section 2)
        var token = await _viewer.OpenDocumentAsync(
            path,
            new Doconut.PdfConfig { AllowSearch = true },
            new DocOptions(),
            CancellationToken.None);

        ViewBag.Token = token;
        ViewBag.BasePath = "doconut";          // matches the middleware branch
        ViewBag.ResPath = "doconut-res/images";// embedded resources
        return View();
    }

    private readonly Doconut.Viewer _viewer;
    public FileController(Doconut.Viewer viewer) => _viewer = viewer;
}

The controller does three things:

  1. Index – reads the App_Data/Docs folder and passes the file names to a view.
  2. Download / Delete – standard MVC actions that use the built‑in PhysicalFile helper.
  3. ViewPdf – opens the selected PDF with Doconut, receives a security token, and passes it to a Razor view.

2. Wire Up Session State and Doconut Services

Doconut’s viewer relies on ASP.NET session state to protect the token that identifies a document. Add the required services in Program.cs (or Startup.cs for .NET 6 projects):

var builder = WebApplication.CreateBuilder(args);

// MVC services
builder.Services.AddControllersWithViews();

// Session cache – required for token validation
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession();

// Doconut registration – point to your license file
builder.Services.AddDoconut(options =>
{
    options.LicensePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "Doconut.Viewer.lic");
    options.MiddlewarePath = "/doconut";
    options.ResourcesPath = "/doconut-res";
    options.UnsafeMode = false; // keep the default security
});

var app = builder.Build();

// Session must run before the viewer middleware
app.UseSession();

// The Doconut middleware must be mapped to the path configured above.
// See Doconut documentation for the exact mapping call (e.g., app.Map("/doconut", …)).

app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());

app.Run();

Key points:

  • AddDistributedMemoryCache supplies the in‑memory store that AddSession needs.
  • UseSession() must appear before the Doconut middleware; otherwise you’ll see the runtime error “Session middleware not configured.”
  • MiddlewarePath and ResourcesPath are coordination strings – the actual middleware branch must match the path you configure.

3. Create the File‑Manager View

Create Views/File/Index.cshtml. It lists files and provides actions for download, delete, and PDF view.

@model List<string>
<h2>Document Library</h2>
<table>
    <thead><tr><th>Name</th><th>Actions</th></tr></thead>
    <tbody>
@foreach (var f in Model)
{
    <tr>
        <td>@f</td>
        <td>
            <a asp-action="Download" asp-route-name="@f">Download</a> |
            <a asp-action="Delete"   asp-route-name="@f">Delete</a> |
            @if (Path.GetExtension(f).Equals(".pdf", StringComparison.OrdinalIgnoreCase))
            {
                <a asp-action="ViewPdf" asp-route-name="@f">View PDF</a>
            }
        </td>
    </tr>
}
    </tbody>
</table>

When the user clicks View PDF, the ViewPdf action creates a token and returns a second view that hosts the viewer widget.


4. Render the Doconut Viewer

Create Views/File/ViewPdf.cshtml. The page only needs a container element; the JavaScript widget is loaded from the embedded resources.

@{
    var token = ViewBag.Token as string;
    var basePath = ViewBag.BasePath as string;
    var resPath = ViewBag.ResPath as string;
}
<h2>PDF Preview</h2>
<div id="div_ctlDoc"></div>

<script src="@($"{basePath}/docViewer.js")"></script>
<link rel="stylesheet" href="@($"{basePath}/docViewer.css")" />

<script>
    $('#div_ctlDoc').docViewer({
        AccessToken: '@token',
        BasePath: '@basePath',
        ResPath: '@resPath',
        FitType: 'width',
        showThumbs: true,
        autoLoad: true,
        onError: function (msg) { console.error(msg); }
    });
</script>

The widget reads the AccessToken you supplied, contacts the /doconut/DocImage.axd endpoint, and streams each page as an image. Because the token is tied to the current ASP.NET session, a user cannot copy the URL and view the document from another browser without the same session cookie.


5. Handling Token Expiration and Security

Doconut’s default token timeout is 60 minutes (configurable via DocOptions.TimeOut). If the viewer receives “Document session not found” or “You Are Not Authorized To View This Page”, the session has either expired or the request is coming from a different browser context.

Typical remedies:

  • Refresh the page – the controller will generate a fresh token.
  • Increase the timeout – pass a custom DocOptions when opening the document:
var options = new DocOptions { TimeOut = TimeSpan.FromHours(2) };
var token = await _viewer.OpenDocumentAsync(
    path,
    new Doconut.PdfConfig { AllowSearch = true },
    options,
    CancellationToken.None);
  • Cross‑origin setups – ensure the SameSite attribute on the session cookie is compatible with your front‑end domain. The default Lax works for most same‑origin portals.

6. Extending the Manager (Optional)

The core file manager is already functional, but you may want to add:

  • Upload – a simple form that posts an IFormFile and saves it to _root.
  • Folder navigation – modify the Index action to accept a relative path and guard it against directory traversal (Path.GetFullPath + StartsWith(_root) check).
  • Search – filter files by name before passing them to the view.

All these extensions stay within the same MVC pattern and continue to benefit from the same session‑protected Doconut viewer.


7. What You Have Achieved

  • A free ASP.NET MVC file manager – no third‑party UI libraries, no licensing fees.
  • In‑browser PDF rendering via Doconut’s universal viewer, avoiding the need for Office or Adobe installations on the client.
  • Secure token‑based access that leverages ASP.NET session state, preventing arbitrary URL sharing.
  • A single code base that works for .NET 6 and .NET 8 and can be ported to Razor Pages or minimal APIs with only the registration calls (AddDoconut, UseSession) changing.

Deploy the app to IIS, Azure App Service, or Docker – the same Program.cs configuration works everywhere because Doconut runs on .NET 6+ and does not require any native Office components.

#ASP.NET MVC#File Manager#PDF Viewer#.NET#Doconut