File upload control in forms (Kentico 12 MVC)

#Kentico development #Kentico Experience #Kentico Kontent

SHARE

 

With earlier versions of Kentico 12 MVC we got a case where we needed to add file upload capabilities to a form, but there was not out of the box feature to implement it, so we came out with our custom file upload control and we thought our experience might be useful to someone else, so we want to share it!


 

With earlier versions of Kentico 12 MVC we got a case where we needed to add file upload capabilities to a form, but there was not out of the box feature to implement it, so we came out with our custom file upload control and we thought our experience might be useful to someone else, so we want to share it!

INTRODUCTION

This step-by-step guide shows how to implement File upload form control Kentico MVC.

In our case, we needed to upload only PDF and MS Word documents, but I’m sure it is quite easy to extend it and allow other document formats.

 

Before diving into technical details, I’d like to provide you with high level view of this process:

  • A user chooses files to be uploaded;

  • Before actual uploading of selected file we will verify file extension and file size with JS;

  • If everything looks good JS converts file to base64 string, as this is the format we use to store file into the database;

  • JS stores base64 string into a hidden field on the form;

  • Send form data to the server upon form submission and store it into the database;
     

Step 1

Let’s create UploadFileComponent class in Models folder - we will need to register it in FormBuilder properties in which we will upload file.


using CMS.SiteProvider;
using Kentico.Forms.Web.Mvc;

[assembly: RegisterFormComponent(
    "UploadFile",
    typeof(UploadFileComponent),
    "UploadFile name",
    Description = "UploadFile description",
    IconClass = "icon-picture")]

    public class UploadFileComponent : FormComponent<UploadFileProperties, string="">
    {
        [BindableProperty]
        public string FileBinary { get; set; } = string.Empty;//the value in which we will upload the //file

        public string SiteName => SiteContext.CurrentSiteName;

        public override string GetValue() => FileBinary;

        public override void SetValue(string value)
        {
            FileBinary = value;
        }
    }



Step 2

On this step let’s create UploadFileProperties class - it defines properties in database which will store file to.


using CMS.DataEngine;
using Kentico.Forms.Web.Mvc;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;



    public class UploadFileProperties : FormComponentProperties<string> //this class create in db property (where you can save file)
    {

        public UploadFileProperties() : base(FieldDataType.LongText)
        {
        }

        [DefaultValueEditingComponent(TextInputComponent.IDENTIFIER)]
        public override string DefaultValue { get; set; }

        [Required]
        public string File { get; set; }

    }

Step 3

Now let’s add FormComponent folder underneath Content folder and create UploadFile folder and create uploader-component.js file within the last one. It is JS code that is responsible for capturing a file on a front end and sending it to a server.


window.your_namespace = window.your_namespace || {};

(function (UploadFileComponent) {

    UploadFileComponent.renderFileDetails = function (target) {

        var mbSize = 5200000;
        var file = target.files[0];

        if (file) {
            var detailsElement =
                target.parentElement.parentElement.querySelector(".kn-upload-file-details");

            var checkFileType = file.name.split('.').pop();

            if (checkFileType === "docx" || checkFileType === "pdf") {
                var fileSize = 0;

                if (file.size > mbSize) {
                    detailsElement.querySelector(".kn-file-size").style.color = "red";
                    detailsElement.querySelector(".kn-file-size").innerHTML =
                        "File size should be less than " + "5" + "MB Your size : " + Math.round(file.size / (1024 * 1024)) + 'MB';      
                    document.querySelector(".form-control.file-upload").value = "";
                } else {

                    fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'kB';

                    detailsElement.querySelector(".kn-file-size").innerHTML =
                        "Size: "
                        + fileSize;

                    if (checkFileType === "docx") {
                        detailsElement.querySelector(".kn-file-type").innerHTML =
                            "Type: application/"
                            + checkFileType;

                    }
                    else {
                        detailsElement.querySelector(".kn-file-type").innerHTML =
                            "Type: "
                            + file.type;
                    }

                    document.querySelector(".form-control.myclass").value = file.name;

                    var pathArray = window.location.pathname.split('/');
                    var lastSegment = pathArray.pop() || pathArray.pop();
                    document.querySelector(".form-control.vacancy-name").value = lastSegment;

                    var reader = new FileReader();
                    reader.onload = (function (theFile) {
                        return function (e) {
                            var binaryData = e.target.result;
                            var base64String = window.btoa(binaryData);
                            document.querySelector(".form-control.file-upload").value = base64String;//  base64 file save into input value 
                        };
                    })(file);
                    reader.readAsBinaryString(file);
                }

            } else {
                detailsElement.querySelector(".kn-file-type").style.color = "red";
                detailsElement.querySelector(".kn-file-type").innerHTML =
                    "Invalid file type. Please upload a .docx or .pdf file.";
            }
            
        }
    };

}(window.your_namespace.UploadFileComponent = window.your_namespace.UploadFileComponent || {}));

Step 4

We need to create FormExtensions folder and create FormExtensions class where we create custom input.


using System.Collections.Generic;
using System.Web.Mvc;



    public static class FormExtensions
    {
       
        public static MvcHtmlString CustomInput(this HtmlHelper helper, string inputType, string name, object value, IDictionary<string, object=""> htmlAttributes)
        {
            TagBuilder tagBuilder = new TagBuilder("input");
            tagBuilder.MergeAttribute("type", inputType);
            tagBuilder.MergeAttribute("name", name);
            tagBuilder.MergeAttribute("value", value.ToString());
            tagBuilder.MergeAttributes(htmlAttributes);

            return new MvcHtmlString(tagBuilder.ToString(TagRenderMode.StartTag));
        }

        public static MvcHtmlString CustomInput(this HtmlHelper helper, string inputType, string name, object value, object htmlAttributes) =>
            CustomInput(helper, inputType, name, value, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));

   
    }

Step 5

We need to create FormComponent folder in Views/Shared folder, then create _UploadFile.cshtml view. In this view we access the javascript functions that uploads a file.


@using CMS.Helpers
@using CMS.MediaLibrary
@using Kentico.Forms.Web.Mvc
@using Models.FormComponent.FileUploader
@using your_namespace.Extension

@model UploadFileComponent
@{

    int GetCurrentPageId()
    {
        var pageBuilder = Context.Kentico().PageBuilder();
        int pageId = -1;

       pageId = feature.PageIdentifier;
    
        return pageId;
    }

    int currentPageId = GetCurrentPageId();

    if (currentPageId == 0)
    {
        Guid guid;
        guid = Guid.TryParse(Model.FileBinary.ToString(), out guid) ? guid : Guid.Empty;
        var mediaFileInfo = MediaFileInfoProvider.GetMediaFileInfo(guid, Model.SiteName);

    }
    else
    {

        string currentFilePathId = Html.IdFor(model => model.FileBinary).ToString();
        IDictionary<string, object=""> fileInputAttributes = ViewData.GetEditorHtmlAttributes();
        fileInputAttributes["onchange"] = $"window.your_namespace.UploadFileComponent.renderFileDetails(this);";

        IDictionary<string, object=""> hiddenInputAttributes = ViewData.GetEditorHtmlAttributes();
        IDictionary<string, object=""> hiddenInputAttributesVacancyName = ViewData.GetEditorHtmlAttributes();

        fileInputAttributes["type"] = "button";

        if (hiddenInputAttributes.ContainsKey("class"))//add to hidden input class
        {
            hiddenInputAttributes["class"] += " file-upload";
          
        }
@Html.CustomInput("file", "UploadFile", string.Empty, fileInputAttributes) @Html.HiddenFor(model => model.FileBinary, hiddenInputAttributes)
 
 
} }

Summary

This brief article shows how to create File upload control in Kentico MVC forms, which saves particular file types with a minimum number of server requests. It does not cover all possible scenarios, but might be a good starting point for anyone that needs similar functionality.

Author

Check other articles

5 5

What our clients say

Bits Orchestra team are outstanding developers‚Äč. They listen carefully to our business needs and easily turns our business objectives into a well thought out and executed development effort. Roman is very bright and definitely the most capable developer that has worked on our site. He is not only a Kentico expert but has successfully tackled other complicated development assignments demonstrating expertise in both front and backend development. Roman takes initiative to suggest enhancements that make site maintenance easier while improving the customer experience. The team is very responsive to our work requests and has great follow up. They have also worked very business partners and this has reflected positively on our company. Roman is a true partner for us and a tremendous asset to our organization. We will continue to work with them and would highly recommend Roman and his team for your development needs. He and his team will exceed your expectations!
 Alan Lehmann
Alan Lehmann
President at In energy sector

What our clients say

The Bits Orchestra team does excellent work. They are always available and I appreciate our frequent calls and screen-shares together. Their dedication to the projects and knowledge of Kentico is outstanding. They truly care about the quality of their work, and became a part of our team easily!
Shena Lowe
Shena Lowe
Managing Partner at Consensus Interactive

What our clients say

We hired Roman for a Kentico analysis project and have been very satisfied. He is very skilled and professional. We are looking to hire him and his team again on future projects.
Sylvain Audet
Sylvain Audet
CEO at MyDevPartner.com

What our clients say

Roman and team have taken over an existing Kentico EMS site for a large US Oil Company. So far, they have handled every single request that we have thrown at them and these were diverse, challenging, often bespoke, usually urgent and almost daily, over the last 11 months. Their work is of an extremely high quality, they are capable, quick and we have great confidence in the support that we are getting.
Jon Hollis
Jon Hollis
Head of Web Development at confidential

What our clients say

Bits Orchestra team was very helpful, they had a good understanding of the brief and deep knowledge of the system. They were always keen to provide advice and recommendations that benefit the project substantially.
Ramon Lapenta
Ramon Lapenta
Senior Front End Developer at Cyber-Duck Ltd