A CKEditor File Browser For ASP.NET Web Pages

CKEditor is one of the most popular rich text editors available. Out of the box, there is no file browser to help with selecting images, but you can purchase an add-on from them - CKFinder. Alternatively, you can build your own file browser component.

By default, if you click the Image button in CKEditor, you are presented with a dialogue box that requires you to enter a url for the image you want to insert. For web developers an other users with a technical bent, that shouldn't pose too many problems, but the average user won't have a clue as to what is required. What they need is a way to select an image and to have its url automatically populate the dialogue. CKEditor exposes an API that enables developers to plug in their own custom file browser to enable this.

At its simplest, a file browser can present a list of files and provide a means by which the user can indicate which one they would like to use. However, this approach would require that the user can identify which image they would like to select purely from its file name. Once any collection of image files grows, it is unlikely that the user will be able to associate images with file names unless some kind of supplementary indexing system was employed. A much more user-friendly solution would be to provide the user with a collection of thumbnails of images from which they can make their selection. The workflow is as follows:

  1. User clicks a button to browse images on the server
  2. User is presented with a list of directories that contain images from which they can select one
  3. The images in the selected directory are presented to the user in thumbnail form
  4. User can select one image by clicking it and the dialogue is populated with their selection

So you need to begin with some code to generate thumbnails of reasonable quality for display. The following code is added to a file called ThumbNails.cshtml which is placed in the App_Code folder:

@using System;
@using System.Drawing;
@using System.Drawing.Drawing2D;
@using System.IO;
@functions {
    public static byte[] GenerateTumbnail(string image, double thumbWidth) {
        try {
            using (var originalImage = Image.FromFile(image)) {
                var oWidth = originalImage.Width;
                var oHeight = originalImage.Height;
                var thumbHeight = oWidth > thumbWidth ? (thumbWidth / oWidth) * oHeight : oHeight;
                thumbWidth = oWidth > thumbWidth ? thumbWidth : oWidth;
                using (var bmp = new Bitmap((int)thumbWidth, (int)thumbHeight)) {
                    bmp.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution);
                    using (var graphic = Graphics.FromImage(bmp)) {
                        graphic.SmoothingMode = SmoothingMode.AntiAlias;
                        graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
                        graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;

                        var rectangle = new Rectangle(0, 0, (int)thumbWidth, (int)thumbHeight);
                        graphic.DrawImage(originalImage, rectangle, 0, 0, oWidth, oHeight, GraphicsUnit.Pixel);
                        var ms = new MemoryStream();
                        bmp.Save(ms, originalImage.RawFormat);
                        return ms.GetBuffer();
        catch (Exception ex) {
            throw (ex);

This method picks up the the original image that is passed into it, and reduces it in size if it exceeds the width passed in as the second argument. It returns a byte array, so the new image is not stored on disk. Next, you need some code that passes in the image and thumbnail width and can do something meaningful with the byte array that is returned. This code forms Thumb.cshtml which resides in the root of the site:

    if (Request["image"].IsEmpty()) {
    var image = Path.Combine(Server.MapPath("~/Media/"), Server.UrlDecode(Request["image"]));
    var thumbWidth = 128D;
    var buffer = ThumbNails.GenerateTumbnail(image, thumbWidth);
    Response.ContentType = string.Format("image/{0}", Path.GetExtension(image).Trim(new[]{'.'}));
    Response.OutputStream.Write(buffer, 0, buffer.Length);

This file acts as a handler. When it is requested, it takes the image file name from the query string and uses the GenerateThumbnail function to create the thumb image and then write it to the browser. For a handler file like this to work, it needs to be set as the src of an image element. That happens in ImageViewer.cshtml:

    Layout = null;
    var directory = new DirectoryInfo(Path.Combine(Server.MapPath("~/Media"), Request["directory"]));
@foreach (var file in directory.GetFilesByExtensions(".png", ".gif", ".jpg")) {
    <div class="thumbnail">
        <img src="/Thumb/?image=@directory.Name\@file.Name" alt="thumb" title="@directory.Name/@file.Name" />

This file is responsible for locating the selected directory, represented by the value passed in the query string, and then obtaining all files that have the specified extensions and then displaying their thumbnail version. The url of the image is set as the value of the title attribute. This is visible to the user as a tooltip when they hover over an image, and is used later to populate the CKEditor image dialogue. GetFilesByExtensions is a custom extension method. It lives in a class file called FileExtensions.cs which is placed in the App_Code folder:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

public static class FileExtensions
    public static IEnumerable<FileInfo> GetFilesByExtensions(this DirectoryInfo directoryInfo, params string[] extensions) {
        var allowedExtensions = new HashSet<string>(extensions, StringComparer.OrdinalIgnoreCase);
        return directoryInfo.EnumerateFiles().Where(f => allowedExtensions.Contains(f.Extension));

The main file is called FileBrowser.cshtml. It is the file that gets called when the user clicks the insert image icon in CKEditor.

    Layout = null;
    var media = new DirectoryInfo(Server.MapPath("~/Media"));
        <title>Media Browser</title>
        <link href="~/Content/StyleSheet.css" type="text/css" rel="stylesheet" />
        <script src="/Scripts/jquery.js" type="text/javascript"></script>
        <script src="/Scripts/ckeditor/ckeditor.js" type="text/javascript"></script>
        <script type="text/javascript">
            var funcNum = @(Request["CKEditorFuncNum"] + ";")
            $(function() {
                $('li').click(function() {
                    $('#fileExplorer').load('/ImageViewer?directory=' + encodeURIComponent($(this).text()));
                }).hover(function() {
                    $(this).css('cursor', 'pointer');
                $('#fileExplorer').on('click', 'img', function () {
                    var fileUrl = '/Media/' + $(this).attr('title');
                    window.opener.CKEDITOR.tools.callFunction(funcNum, fileUrl);
                }).hover(function() {
                    $(this).css('cursor', 'pointer');
        <div id="folderExplorer">
            @foreach (var dir in media.EnumerateDirectories().Where(d => d.GetFilesByExtensions(".png", ".gif", ".jpg").Any())) {
       <div id="fileExplorer"></div>

The main images folder is called Media, and images are stored in subfolders. These are displayed in a list. Some jQuery is used to set the cursor to a pointer (hand) when you hover over the list items, indicating that they are clickable. When they are clicked, the empty fileExplorer div is loaded with the result of a request to ImageViewer.cshtml - which is the file that calls Thumb.cshtml to display thumbnail versions of the contents of the selected directory.

The next block of jQuery applies a click event handler to any image that appears in the fileExplorer div. It extracts the url of the image being clicked from its title attribute. It passes that value along with another originally sent to the FileBrowser file by CKEditor back to the editor so that it knows you are selecting a file. The url of the file is applied to the appropriate dialogue text box and then the filebrowser window is closed.

All you need to do now is tell CKEditor that you have a file browser, and where it is. You can do that in the file that features the editor. Here's a minimal file that only displays the editor and configures the file browser:


<!DOCTYPE html>

<html lang="en">
        <meta charset="utf-8" />
        <title>CKEditor File Browser FOR ASP.NET Web Pages</title>
        <link href="~/Content/StyleSheet.css" type="text/css" rel="stylesheet" />
        <script type="text/javascript" src="~/Scripts/jquery.js"></script>
        <script type="text/javascript" src="~/Scripts/ckeditor/ckeditor.js"></script>
            $(function () {
                CKEDITOR.replace('Content', {
                    filebrowserBrowseUrl: '/FileBrowser',
                    filebrowserWindowWidth: 800,
                    filebrowserWindowHeight: 600
        <div id="form">
            <form method="post">
                    <input type="submit" />

The file browser configuration requires a url to be passed to the filebrowserBrowseUrl parameter, and then optionally a height and width for the window that will open to display the resource at the specified url. If you do not specify a height or width, the default values of 80% of the available screen width, and 70% of the available height will be used.

This example, which is available at GitHub assumes that the images are in sub folders of the Media folder. The download also includes a version that works with just one images folder. You can access that version by downloading and running the site and requesting Default2.cshtml.


Date Posted:
Last Updated:
Posted by:
Total Views to date: 25559


- Andrew Davis

Hi Mike,
you can replace the 'GenerateTumbnail' method with new 'WebImage' class found in 'System.Web.Helpers'. This will also enable you to do all the resizing and cropping in a fluent manner.

- Mike


I decided against using the WebImage helper as the quality of the resulting image is not very good.

- Handy


Is there an example to use this on asp.net web forms ?

- Mesut

I used your tutorial in my site, then I saw that you used TinyMCE in your CMS. Could you please prepare a tutorial for TinyMCE?

- Mike


I use CKEditor in the CMS. I've never really looked at TinyMCE.

Popular Pages

More Info

Recent Comments

KrASh 5/19/2015 11:10 PM
In response to Adding Search
Hello Mike, I have a question about the second search. In one line it has a where with the title and...

KrASh 5/19/2015 11:04 PM
In response to Accessing Your Model's Data from a Controller
Thanks, I had some silly mistake, can't remember now what it was (had some personal problems last i...

Satyabrata Mohapatra 5/18/2015 4:29 PM
In response to Error Handling in ASP.NET Razor Web Pages
Great article. Learned a lot....

Corstian Boerman 5/13/2015 12:59 PM
In response to ASP.NET 5 Middleware, Or Where Has My HttpModule Gone?
It looks as if this technique doesn't work anymore. (As of the VS 2015 RC release) When adding it...

Alvin 5/12/2015 1:20 PM
In response to Razor Web Pages E-Commerce - Adding A Shopping Cart To The Bakery Template Site
Maybe ability to update quantity of the items once you are in the "Review Cart" step. Also, if on...

Rakesh Panchal 5/12/2015 5:02 AM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
this is v v good web site for help...

Mariusz 5/11/2015 7:19 PM
In response to Introducing TagHelpers in ASP.NET MVC 6
In your comparison of old Html.RouteLink and new AnchorTagHelper where we can specify "title" and in...

Andrea 5/11/2015 3:40 PM
In response to Save And Retrieve Files From a Sql Server CE Database with WebMatrix
Please excuse my noob question. I am using your tips to add file upload to a form I have created. a...

Luis 5/10/2015 5:26 PM
In response to Accessing Your Model's Data from a Controller
Ok. I got it! Return RedirectToAction("Create") on the controller :-) thanks...

Luis 5/10/2015 4:08 PM
In response to Accessing Your Model's Data from a Controller
Hi. Great tutorial. I'm starting to learn MVC. What can I do for when the Create Button is it...