Using Summernote In ASP.NET MVC (Saving Image Files Separately)

Introduction

As mentioned in my last post, I ended up creating my own blog engine inspired by the working press for this site. So I thought, "Why not write my first blog about the main challenge I faced while developing that engine and how I solved it?" By now you might have understood why I chose to write my own blog engine, yes, to blog further about it!

So the main component of any blog engine. It is the blog writing screen, which is basically a WYSIWYG HTML editor. For those who live under a rock and don't know the full form of WYSIWYG, it's "What You See Is What You Get." The term is pretty old from the days of the FrontPage editor. For writing this screen, I went through the code of many. N-based open-source blog engines like miniblog, Dasblog, etc. Somehow, I was not very comfortable with anything used in them, until I read this article where ''the author'' compared all JavaScript WYSIWYG editors and declared Summernote as the clear winner, so I decided to go with it. However, when I implemented Summernote in my blog application, I found an issue with it. Summernote actually embeds the images uploaded as content in base64 format inside the content, which makes the content very heavy and unmanageable (if the content has many images like this blog), especially when we have to store the content in the database.

On looking into Summernote documentation I found that we can solve this problem by capturing the '''' event in jQuery and using ajax to upload the image separately to a folder on the server and change the base64 image source to a URL. In this blog, I will explain the step-by-step procedure to do this, so let's get started.

Step 1. Create a new ASP.NET MVC project in Visual Studio ( yes it's still ASP.Net, not Core ) by following the steps mentioned in this article.

Step 2. Create the BlogPost Model as per the following code in the Models folder.

public class BlogPost  
{  
    /// <summary>  
    /// For Title of the Note  
    /// </summary>  
    public string Title { get; set; }  
    /// <summary>  
    /// To Save content of the Note which can be anything.  
    /// </summary>  
    [AllowHtml]  
    public string Content { get; set; }  
}  

Step 3. Add a new controller named SummerNoteEx and Change the Index method to BlogEntry like the following code.

using System;
using System.IO;
using System.Web;
using System.Web.Mvc;
using TrWebBlogEx.Models;

namespace TrWebBlogEx.Controllers
{
    public class SummerNoteExController : Controller
    {
        // GET: SummerNoteEx
        public ActionResult BlogEntry()
        {
            return View();
        }
    }
}

Step 4. Right-click on BlogEntry Action Method and Click on Add View Option as shown in the following screenshot.

 BlogEntry

Step 5. Download Summernote from this URL by clicking on the Download compiled button as shown in the following screenshot.

Summernote

Step 6. Extract the downloaded zip file, Rename the dist folder to Summernote, and copy it into the Scripts folder as shown in the below Image.

Scripts folder

Step 7. Update the View as per the following code.

@model TrWebBlogEx.Models.BlogPost
@{
    ViewBag.Title = "Blog Entry";
}
<link href="~/Scripts/summernote/summernote.css" rel="stylesheet" />
<script src="~/Scripts/summernote/summernote.js"></script>
<h2>Index</h2>
<p class="panel panel-primary">
    <p class="panel-heading panel-head">Add Note</p>
    <p class="panel-body">
        @using (Html.BeginForm())
        {
            <p class="form-horizontal">
                <p class="form-group">
                    @Html.LabelFor(model => model.Title, new { @class = "col-lg-2 control-label" })
                    <p class="col-lg-9">
                        @Html.TextBoxFor(model => model.Title, new { @class = "form-control" })
                    </p>
                </p>
                <p class="form-group">
                    @Html.LabelFor(model => model.Content, new { @class = "col-lg-2 control-label" })
                    <p class="col-lg-9">
                        @Html.TextAreaFor(model => model.Content, new { @class = "form-control", @row = 5 })
                    </p>
                </p>
                <p class="form-group">
                    <p class="col-lg-9"></p>
                    <p class="col-lg-3">
                        <button class="btn btn-success" id="btnSubmit" type="submit">
                            Submit
                        </button>
                    </p>
                </p>
            </p>
        }
    </p>
</p>
<script>
    $('#Content').summernote({
        height: 300,                    // set editor height
        minHeight: null,                // set minimum height of editor
        maxHeight: null,                // set maximum height of editor
        focus: true
    });
</script>

Step 8. Update the Controller with the following code, this will show the content back on the page.

[HttpPost]
public ActionResult BlogEntry(BlogPost aBlogPost)
{
    return View("ShowBlog", aBlogPost);
}

Step 9. Now execute the application, click on the Picture button like the Below Image.

Application

Step 10. Once the Image is uploaded, click on the Code button Below.

Index

It will show the following screen. See the Base64 code highlighted as shown below.

 Base64 code

To solve this problem update the JavaScript section of BlogEntry View with the following code.

<script>
    $('#Content').summernote({
        height: 300,                    // set editor height
        minHeight: null,                // set minimum height of editor
        maxHeight: null,                // set maximum height of editor
        focus: true,                    // set focus to editable area after initializing summernote
        callbacks: {
            onImageUpload: function (files) {
                for (let i = 0; i < files.length; i++) {
                    UploadImage(files[i]);
                }
            }
        }
    });

    function UploadImage(file) {
        var url = '@Url.Action("UploadFile", "SummerNoteEx")';

        formData = new FormData();
        formData.append("aUploadedFile", file);
        $.ajax({
            type: 'POST',
            url: url,
            data: formData,
            cache: false,
            contentType: false,
            processData: false,
            success: function (FileUrl) {
                // alert(FileUrl);
                var imgNode = document.createElement('img');
                imgNode.src = FileUrl;
                $('#Content').summernote('insertNode', imgNode);
            },
            error: function (data) {
                alert(data.responseText);
            }
        });
    }
</script>

In the above code, we are capturing the onImageUpload event of Summernote, reading the uploaded file, saving that file on server by using the UploadFile ActionMethod of controller (Below Code), getting back its server path and updating the path in Summernote to save the image URL instead of base64 data.

[AcceptVerbs(HttpVerbs.Post)]
public JsonResult UploadFile(HttpPostedFileBase aUploadedFile)
{
    var vReturnImagePath = string.Empty;
    if (aUploadedFile.ContentLength > 0)
    {
        var vFileName = Path.GetFileNameWithoutExtension(aUploadedFile.FileName);
        var vExtension = Path.GetExtension(aUploadedFile.FileName);

        string sImageName = vFileName + DateTime.Now.ToString("yyyyMMddHHmmss");

        var vImageSavePath = Server.MapPath("/UpImages/") + sImageName + vExtension;
        vReturnImagePath = "/UpImages/" + sImageName + vExtension;
        ViewBag.Msg = vImageSavePath;
        var path = vImageSavePath;

        // Saving Image in Original Mode
        aUploadedFile.SaveAs(path);
        var vImageLength = new FileInfo(path).Length;
        // here to add Image Path to Your Database
        TempData["message"] = string.Format("Image was Added Successfully");
    }
    return Json(Convert.ToString(vReturnImagePath), JsonRequestBehavior.AllowGet);
}

Now, when we execute the application and follow Steps 8 and 9 again to view the code, we will see the Image URL in the code instead of Base64 data like in the following.

URL

This way, I made my blog content smaller to save in the database and also increased the image re-usability. This was how I created a blog entry screen for my blog engine, the code for this blog and all web blogs (which I may write in the future) are consolidated into a single web application code uploaded here at Github.

Thanks for reading and until next time, keep smiling and progressing!


Similar Articles