Friday, November 29, 2013

Checking file size before uploading ExtJs /Javascript

Hello everyone,

In my last post I have explained the multi file upload control created in ExtJs. I have enhanced that controls to a further level as per my requirements. We can check file types , file duplication etc in the client side. We can restrict the user from adding certain file extensions. In this post I'm gonna explain how can we check the file size and restrict the user from adding a file of size more than a configured value ( say 2Mb). There are ways to check file size in the client side .

1. Use HTML5 File API.
This will work only in browsers which have HTML support ( IE 10 onwards , Chrome , FF latest versions). (IE 8 and 9 will not support this )
2. In IE ( older versions 9 below) use ActiveX objects.
- This may not work if browsers settings are not configured accordingly. In secure environment users browser might not have permission to run ActiveX objects

What we can do to check the file size in a cross-browser way.

The idea is to pass the added file to server and check the size in server and return the file size and success value . Don't worry about thinking what happens if we pass a 1GB file. It will not pass to the server anyway, instead you will get exception response like content length exceeded  from network transport layer and you can catch the exception and reject the file as well.

This is nothing to do with ExtJs. We can use this with any javascript framework. I am using ExtJs framework.

See how it works !




In the "change" event of filefield control we will first validate the file for allowed extensions , then validate if it is already added or not. if these to validations passed then the file will be sent to a webform ( say filechecker.aspx) and wait for the response. If the response contains success: true then it will be added if false error message will be displayed. Meanwhile we will try to decode the Json returned from the server. if decode failed means some error in between which is nothing but http content exception.

I have implemented one more validation checking which restrict the user from adding many files with a combined size limit exceeds a configured value ( say 20Mb) like in emails. You may not be allowed to add files of total size more than say 20Mb.


Code snippet

I have pasted the code only for your idea.

In the screen shot you can see two buttons one is for adding files another is for adding hyperlink. This is my requirement. Nothing in it ! . Clicking on the add attachment button will open file dialogue and upon selecting the file you will get validation message if the validation fails otherwise the file will be added.

So Don't confuse with other stuff in my code

 Just take a look at the ValidateFileSize() function

 /* File Created: August 19, 2013 */  
 /* Author : Sebastian */  
 Ext.define("PrIns.controls.AttachmentControl", {  
   extend: 'Ext.form.Panel',  
   fileUpload: true,  
   border: 0,  
   alias: 'widget.attachcontrol',  
   requires: [  
     'PrIns.controls.Attachments',  
     'Ext.MessageBox'  
   ],  
   margins: '2 2 2 2',  
   accept: ['pdf', 'jpg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'bmp', 'tif', 'zip', 'ppt', 'pptx', 'txt', 'csv', 'rar', 'xps'],  
   frame: false,  
   layout: {  
     type: 'hbox'  
   },  
   items: [  
           {  
             xtype: 'fieldcontainer',  
             layout: 'vbox',  
             items: [  
                  {  
                    xtype: 'filefield',  
                    buttonOnly: true,                    
                    flex: 1,  
                    tooltip: '',  
                    listeners: {  
                      change: function (view, value, eOpts) {  
                        // alert(value);  
                        var parent = this.up('form');  
                        parent.onFileChange(view, value, eOpts);  
                      }  
                    },  
                    buttonConfig: {  
                      text: '',  
                      width: 25,  
                      iconCls: 'add-attachment-btn',  
                      iconAlign: 'center',  
                      tooltip: 'Translate(Add Attachment)',  
                    }  
                  },  
               {  
                 xtype: 'button',  
                 width: 25,  
                 iconAlign: 'center',  
                 tooltip: 'Translate(Add Hyperlink)',  
                 handler: function (button) {  
                   var handleFunction = function (btn, text, cfg) {  
                     if (btn == 'ok' && Ext.isEmpty(text)) {  
                       var newMsg = '<span style="color:red">Translate(Please enter hypelink):</span>';  
                       Ext.Msg.show(Ext.apply({}, { msg: newMsg }, cfg));  
                     }  
                     if (btn == 'ok' && !Ext.isEmpty(text)) {                      
                       var form = button.up('form');  
                       var Id = form.getFirstItem();  
                       var fileItem = [{ 'Id': Id, "FileName": text, 'AttachmentType': 'Url' }];  
                       form.addItem(fileItem);  
                     }  
                   };  
                   var customPrompt = function (title, msg, fn) {  
                     Ext.MessageBox.show({  
                       title: title,  
                       msg: msg,  
                       buttons: Ext.MessageBox.OKCANCEL,  
                       fn: fn,  
                       minWidth: 400,  
                       prompt: true  
                     });  
                     return Ext.MessageBox;  
                   };  
                   customPrompt('Translate(Add Hyperlink)', 'Translate(Please enter hypelink):', handleFunction);  
                 },  
                 iconCls: 'add-hyperlink-btn',  
                 flex: 1  
               }  
             ]  
           },  
         {  
           xtype: 'tbseparator',  
           margin: '0 3 0 3'  
         },  
          {  
          xtype: 'attachments',  
          flex: 1  
          }  
   ],  
   constructor: function (config) {  
     this.fileslist = [];  
     this.totalSizeLimit = 0;  
     this.callParent(arguments);  
   },  
   setControlValue: function (data) {  
     //debugger;  
     var me=this;     
     var control = Ext.ComponentQuery.query('[xtype=attachments]', this);  
     if (!Ext.isEmpty(control)) {  
       control[0].setControlValue(data);  
     }  
   },  
   disableControl: function (disabled) {  
     var fieldContainer = Ext.ComponentQuery.query('[xtype=fieldcontainer]', this);  
     if (!Ext.isEmpty(fieldContainer)) {  
       fieldContainer = fieldContainer[0];  
       var attachBtn = fieldContainer.items.items[0];  
       var hyerLinkBtn = fieldContainer.items.items[1];  
       attachBtn.setDisabled(disabled);  
       hyerLinkBtn.setDisabled(disabled);  
       if (disabled) {  
         attachBtn.buttonConfig.tooltip = '';  
         hyerLinkBtn.setTooltip('');  
       }  
       else {  
         attachBtn.buttonConfig.tooltip = 'Translate(Add Attachment)';  
         hyerLinkBtn.setTooltip('Translate(Add Hyperlink)');  
       }  
     }  
   },  
   getControlValue: function () {  
     var control = Ext.ComponentQuery.query('[xtype=attachments]', this);  
     if (!Ext.isEmpty(control)) {  
       return control[0].getControlValue();  
     }  
   },  
   getFirstItem: function () {  
     var control = Ext.ComponentQuery.query('[xtype=attachments]', this);  
     if (!Ext.isEmpty(control)) {  
       return control[0].getFirstItem();  
     }  
   },  
   addItem: function (itemArray) {  
     var control = Ext.ComponentQuery.query('[xtype=attachments]', this);  
     if (!Ext.isEmpty(control)) {  
       return control[0].addItem(itemArray);  
     }  
   },  
   hideHyperlink: function () {  
     var fieldContainer = Ext.ComponentQuery.query('[xtype=fieldcontainer]', this);  
     if (!Ext.isEmpty(fieldContainer)) {  
       fieldContainer = fieldContainer[0];  
       var hyerLinkBtn = fieldContainer.items.items[1];  
       hyerLinkBtn.hide();  
     }  
   },   
   setDisabled: function (disabled) {  
     var fieldContainer = Ext.ComponentQuery.query('[xtype=fieldcontainer]', this);  
     if (!Ext.isEmpty(fieldContainer)) {  
       fieldContainer = fieldContainer[0];  
       var attBtn = fieldContainer.items.items[0];  
       var hypBtn = fieldContainer.items.items[1];  
       attBtn.setDisabled(disabled);  
       hypBtn.setDisabled(disabled);  
     }  
     var control = Ext.ComponentQuery.query('[xtype=attachments]', this);  
     if (!Ext.isEmpty(control)) {  
       control[0].hideDeleteButton();  
     }  
   },  
   onFileChange: function (view, value, eOpts) {  
    // debugger;  
     var me = this;     
     var filename = me.extractFileName(value);  
     var IsValid = this.fileValidiation(view, filename);  
     if (!IsValid) { return; }  
     me.validateFileSize(view, filename)  
   },  
   validateFileSize: function (view, filename) {  
     // debugger  
     var me = this;  
     me.setLoading('Translate(Checking file...)');  
     me.getForm().submit({  
       url: 'FilesizeChecker.aspx',  
       params: { MaxSize: 2 },  
       success: function (form, action) {  
         me.OnafterValidation(view, filename,action.response.responseText);  
       },  
       failure: function (form, action) {  
         me.OnafterValidation(view, filename,action.response.responseText);  
       }  
     });  
   },  
   OnafterValidation: function (view, filename,reponseJson) {  
     var me = this;  
     if (!Ext.isEmpty(reponseJson))  
     {  
       var success = false;  
       try {  
         reponseJson = Ext.JSON.decode(reponseJson);  
         success = reponseJson.success;  
       } catch (e) {  
       }  
       //debugger;  
       var maxSize = PrIns.getApplication().Configuration.MaxFileSizeMB;  
       var totalFilesizeMb = PrIns.getApplication().Configuration.TotalFileSizeMB;  
       var totalSizeAllowed = totalFilesizeMb * 1024 * 1024;  
       if (!success) {  
         var erroMsg = Ext.String.format('Translate(The file {0} exceeds the maximum allowed size limit [{1}MB])', filename, maxSize);  
         var messageBox = Ext.MessageBox.show({  
           title: 'Translate(Add Attachment)',  
           msg: erroMsg,  
           buttons: Ext.Msg.OK,  
           icon: Ext.Msg.ERROR  
         });  
         view.setRawValue(null);  
         view.reset();  
         me.setLoading(false);  
       }  
       else {  
         // debugger  
         var nBytes = reponseJson.fileSize;  
         me.totalSizeLimit = me.totalSizeLimit + nBytes;  
         var filesize;  
         for (var aMultiples = ["KB", "MB", "GB"], nMultiple = 0, nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {  
           filesize = nApprox.toFixed(2) + " " + aMultiples[nMultiple];  
         }  
         if (nBytes < 1024) {  
           filesize = nBytes + " Bytes"  
         }  
         filename = filename + ' (' + filesize + ')';  
         if (me.totalSizeLimit > totalSizeAllowed) {  
           var totalSize;  
           for (var aMultiples = ["KB", "MB", "GB"], nMultiple = 0, nApprox = me.totalSizeLimit / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {  
             totalSize = nApprox.toFixed(2) + " " + aMultiples[nMultiple];  
           }  
           var erroMsg = Ext.String.format('Translate(Total file size limit [{0} MB] exceeded.The file {1} cannot be attached.)', totalFilesizeMb, filename);  
           var messageBox = Ext.MessageBox.show({  
             title: 'Translate(Add Attachment)',  
             msg: erroMsg,  
             buttons: Ext.Msg.OK,  
             icon: Ext.Msg.ERROR  
           });  
           view.setRawValue(null);  
           view.reset();  
           me.totalSizeLimit = me.totalSizeLimit - nBytes;  
           me.setLoading(false);  
           return;  
         }  
         me.fileslist.push(filename);  
         var addedFilePanel = Ext.create('Ext.form.Panel', {  
           frame: false,  
           hidden: true,  
           border: 0,  
           padding: 2,  
           margin: '0 10 0 0',  
           layout: {  
             type: 'hbox',  
             align: 'middle'  
           },  
           items: []  
         });  
         var Id = me.getFirstItem();  
         addedFilePanel.setTitle(Id);  
         var newUploadControl = Ext.create('Ext.form.FileUploadField', {  
           buttonOnly: true,  
           buttonConfig: {  
             text: '',  
             width: 25,  
             iconCls: 'add-attachment-btn',  
             iconAlign: 'center',  
             tooltip: 'Translate(Add Attachment)',  
           },  
           listeners: {  
             change: function (view, value, eOpts) {  
               var parent = this.up('form');  
               parent.onFileChange(view, value, eOpts);  
             }  
           }  
         });  
         view.hide();  
         addedFilePanel.add(view);  
         // alert(Id);  
         var fileItem = [{ 'Id': Id, "FileName": filename, 'AttachmentType': 'File', "FileSize": nBytes }];  
         me.addItem(fileItem);  
         var fieldContainer = Ext.ComponentQuery.query('[xtype=fieldcontainer]', me);  
         if (!Ext.isEmpty(fieldContainer)) {  
           fieldContainer = fieldContainer[0];  
           fieldContainer.insert(0, newUploadControl);  
         }  
         me.add(addedFilePanel);  
         me.setLoading(false);  
       }  
     }  
   },  
   fileValidiation: function (me, filename) {  
     var isValid = true;  
     var indexofPeriod = me.getValue().lastIndexOf("."),  
            uploadedExtension = me.getValue().substr(indexofPeriod + 1, me.getValue().length - indexofPeriod);  
     if (!Ext.Array.contains(this.accept, uploadedExtension.toLowerCase())) {  
       isValid = false;  
       // Add the tooltip below to   
       // the red exclamation point on the form field  
       var erroMsg = Ext.String.format('Translate(Please upload files with an extension of {0} only!)', this.accept.join());  
       me.setActiveError(erroMsg);  
       // Let the user know why the field is red and blank!  
       var messageBox = Ext.MessageBox.show({  
         title: 'Translate(Add Attachment)',  
         msg: erroMsg,  
         buttons: Ext.Msg.OK,  
         icon: Ext.Msg.WARNING  
       });       
       me.setRawValue(null);  
       me.reset();  
     }  
     for (var i = 0; i < this.fileslist.length; i++) {  
       if (this.fileslist[i].indexOf(filename) !== -1) {  
         isValid = false;  
         var erMsg = Ext.String.format('Translate(The file {0} already added)!', filename);  
         me.setActiveError(erMsg);  
         var messageBox = Ext.MessageBox.show({  
           title: 'Translate(Add Attachment)',  
           msg: erMsg,  
           buttons: Ext.Msg.OK,  
           icon: Ext.Msg.INFO  
         });  
         me.setRawValue(null);  
         me.reset();  
         break;  
       }  
     }    
     return isValid;  
   },  
   hasAttachments: function () {  
     if (!Ext.isEmpty(this.fileslist)) {  
       return true;  
     }  
     return false;  
   },  
   extractFileName: function (value) {  
     var fileNameIndex = value.lastIndexOf("/") + 1;  
     if (fileNameIndex == 0) {  
       fileNameIndex = value.lastIndexOf("\\") + 1;  
     }  
     var filename = value.substr(fileNameIndex);  
     filename = filename.replace('"', '');  
     filename = filename.replace('"', '');  
     return filename;  
   }  
 });  


Here FilesizeChecker.aspx code

 using System;  
 using System.Collections.Generic;  
 using System.IO;  
 using System.Linq;  
 using System.Net.Http;  
 using System.Web;  
 using System.Web.Configuration;  
 using System.Web.UI;  
 using System.Web.UI.WebControls;  
 namespace xxxxx  
 {  
   public partial class FilesizeChecker : System.Web.UI.Page  
   {  
     protected void Page_Load(object sender, EventArgs e)  
     {  
       int Max_Size_In_Mb = 2; //default  
       try  
       {  
         Max_Size_In_Mb = int.Parse(WebConfigurationManager.AppSettings["MaxFileSizeMB"]);  
       }  
       catch (Exception)  
       {  
       }  
       long MaxSizeBytes = Max_Size_In_Mb * 1024 * 1024;  
       long fileSize = 0;  
       bool success = true;  
       for (int i = 0; i < Request.Files.Count; i++)  
       {  
         HttpPostedFile file = Request.Files[i];  
         if (!string.IsNullOrEmpty(file.FileName))  
         {  
           BinaryReader reader = new BinaryReader(file.InputStream);  
           fileSize = (long)file.InputStream.Length;  
           if (fileSize > MaxSizeBytes)  
           {  
             success = false;  
             break;  
           }  
         }  
       }  
       string json = "{success:true,fileSize:" + fileSize + "}";  
       if (!success)  
       {  
         json = "{success:false,fileSize:" + fileSize + "}";  
       }  
       Response.Clear();  
       Response.ContentType = "application/json; charset=utf-8";  
       Response.Write(json);  
       Response.End();  
     }  
   }  
 }  



Happy coding !


Tuesday, June 18, 2013

ExtJs Multiple file Upload Control

In this post I'm gonna explain a multiple file upload control developed myself in ExtJs. I had a tough time searching for a multi file uploader in ExtJs. But I couldn't find any such control as per my requirement. Finally I did develop a control, which I wanna share with you guys.

I am using ExtJs normal single file upload control. Upon file selected the file upload control will be pushed down and make it hidden in the below panel. At the same time another panel will be displayed below , which will show the filename, attach icon and a remove button. Another file upload control will be created for the user to select next file . This process will get repeated and the user can put as many files as he can in the form. Finally when the form get submitted you will get these files in the server side.

You can remove added files as well. I have added file validation before adding the file too.

You can add or remove file extensions as you needed in the accept property array defined here

 Here is the javascript code

/* File Created: June 14, 2013 */
/* Author : Sebastian */
Ext.define("YourApp.view.util.Multiupload", {
    extend: 'Ext.form.Panel',
    border: 0,
    alias: 'widget.multiupload',
    margins: '2 2 2 2',
    accept: ['pdf', 'jpg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'bmp', 'tif', 'zip'],
    fileslist: [],
    frame: false,
    items: [
        {
            xtype: 'filefield',
            buttonOnly: true,
            listeners: {
                change: function (view, value, eOpts) {
                    //  alert(value);
                    var parent = this.up('form');
                    parent.onFileChange(view, value, eOpts);
                }
            }

        }

    ],
    onFileChange: function (view, value, eOpts) {
        // debugger;
        var fileNameIndex = value.lastIndexOf("/") + 1;
        if (fileNameIndex == 0) {
            fileNameIndex = value.lastIndexOf("\\") + 1;
        }
        var filename = value.substr(fileNameIndex);

        var IsValid = this.fileValidiation(view, filename);
        if (!IsValid) {
            return;
        }


        this.fileslist.push(filename);
        var addedFilePanel = Ext.create('Ext.form.Panel', {
            frame: false,
            border: 0,
            padding: 2,
            margin: '0 10 0 0',
            layout: {
                type: 'hbox',
                align: 'middle'
            },
            items: [

                {
                    xtype: 'button',
                    text: null,
                    border: 0,
                    frame: false,
                    iconCls: 'button-close',
                    tooltip: 'Remove',
                    listeners: {
                        click: function (me, e, eOpts) {
                            var currentform = me.up('form');
                            var mainform = currentform.up('form');
                            var lbl = currentform.down('label');
                            mainform.fileslist.pop(lbl.text);
                            mainform.remove(currentform);
                            currentform.destroy();
                            mainform.doLayout();
                        }
                    }
                },
                {
                    xtype: 'label',
                    padding: 5,
                    listeners: {
                        render: function (me, eOpts) {
                            me.setText(filename);
                        }
                    }
                },
                {
                    xtype: 'image',
                    src: 'assets/images/attach.png'

                }
            ]
        });

        var newUploadControl = Ext.create('Ext.form.FileUploadField', {
            buttonOnly: true,
            listeners: {
                change: function (view, value, eOpts) {

                    var parent = this.up('form');
                    parent.onFileChange(view, value, eOpts);
                }
            }
        });
        view.hide();
        addedFilePanel.add(view);
        this.insert(0, newUploadControl);
        this.add(addedFilePanel);


        // alert(filename);
    },

    fileValidiation: function (me, filename) {

        var isValid = true;
        var indexofPeriod = me.getValue().lastIndexOf("."),
            uploadedExtension = me.getValue().substr(indexofPeriod + 1, me.getValue().length - indexofPeriod);
        if (!Ext.Array.contains(this.accept, uploadedExtension)) {
            isValid = false;
            // Add the tooltip below to
            // the red exclamation point on the form field
            me.setActiveError('Please upload files with an extension of :  ' + this.accept.join() + ' only!');
            // Let the user know why the field is red and blank!
            Ext.MessageBox.show({
                title: 'File Type Error',
                msg: 'Please upload files with an extension of :  ' + this.accept.join() + ' only!',
                buttons: Ext.Msg.OK,
                icon: Ext.Msg.ERROR
            });
            // Set the raw value to null so that the extjs form submit
            // isValid() method will stop submission.
            me.setRawValue(null);
            me.reset();
        }

        if (Ext.Array.contains(this.fileslist, filename)) {
            isValid = false;
            me.setActiveError('The file ' + filename + ' already added!');
            Ext.MessageBox.show({
                title: 'Error',
                msg: 'The file ' + filename + ' already added!',
                buttons: Ext.Msg.OK,
                icon: Ext.Msg.ERROR
            });
            // Set the raw value to null so that the extjs form submit
            // isValid() method will stop submission.
            me.setRawValue(null);
            me.reset();
        }


        return isValid;
    },
});

once this javascript file created in your view . you can put it in your form with an xtype 'multiupload'

Here is the screen shot

Cheers ! Happy coding !