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: Thursday, May 16, 2013 2:00 PM
Last Updated:
Posted by: Mikesdotnetting
Total Views to date: 22444


Tuesday, July 16, 2013 12:49 PM - 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.

Wednesday, July 17, 2013 7:50 PM - Mike


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

Thursday, July 24, 2014 7:51 AM - Handy


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

Friday, October 31, 2014 10:43 AM - 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?

Friday, October 31, 2014 12:34 PM - Mike


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

If you have any comments to make about this article, please use this form to do so. Make sure that your comment relates specifically to the article above. More general comments can be posted through the form on the Contact page.

Please note, all comments are moderated, and some may not be published. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Not relevant to the article
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam
  • Anything in a language I don't understand including gibberish.

I do not pass email addresses on to spammers, so a valid one will assist me in responding to you personally if required.

Recent Comments

stephen 1/28/2015 9:53 PM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
Hi, Sorry for wasting your time, I had a comment <!--Images--> inside the form this stopped it from...

stephen 1/28/2015 9:47 PM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
Everything has been ok up until this part, here's a snippet of the entire file but i removed lines =...

Stephen kenny 1/28/2015 8:26 PM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
Hi, great article. My issue is with the create view. @using (Html.BeginForm("Create", "Reptiles",...

Jenelyn 1/27/2015 6:44 PM
In response to MVC 5 with EF 6 in Visual Basic - Implementing Basic CRUD Functionality
I'm searching CRUD using VB.net 2010 and I can't find any, how sad...

Kadu Portugal 1/23/2015 10:55 AM
In response to Scheduled Tasks In ASP.NET With Quartz.Net
Hi Mike, Thank you for your useful article. I implement it in my project, and everything run...

Satyabrata 1/23/2015 6:02 AM
In response to Usage of the @ (at) sign in ASP.NET
Refreshed a lot of things.Thanks...

Vo Thanh Chinh Trung 1/22/2015 4:30 PM
In response to Adding A View
It's very helpful...Thank you so much, Mikes!...

Joop Stringer 1/22/2015 12:00 PM
In response to Inline Editing With The WebGrid
How to add a row for a new record ?...

Gaetan C 1/21/2015 8:49 PM
In response to Looking At The WebMatrix WebGrid
I use the webgrid very often and I Wonder if it's possible to merge to different sql query into on...

Teresa 1/19/2015 3:08 PM
In response to MVC 5 with EF 6 in Visual Basic - Creating an Entity Framework Data Model
At the top, the first thing you write is "Open Visual Studio and create a new C# Web project named I...