Asp.net mvc Ajax File Uploading using JQuery
Monday, August 26, 2013 by Ehsan Sajjad 4 Comments A+ a-
CodeProject
Using JQuery to post standard forms is extremely simple, but when posting multi-part forms for uploading files it's not so intuitive. This is due to browser security restrictions and sandboxing. Today I'm going to show you how to fake an asynchronous upload without reloading the page and get a result back from the server.
First Idea that came in my mind was using mvc helper Ajax.BeginForm and i came to know that it not supports file upload by partial post-back, it will do post-back.
Second Idea that was in my mind is to use JQuery and AJAX to post file on server so that i can avoid post-back, i tried my own way by posting file and returning partial view via AJAX but same issue was happening the partial view was rendering independently not on the same view, then i searched on internet and found a way.
This method uses IFrame and do a fake async postback and it looks like that file uplaoding via ajax, as our view remains same.
For this post i am storing the state in Session:
Now we can start creating our JavaScript to handle our form posts. The UploadImage() method isn't absolutely necessary since all our example does is post the form, however as in my case i am displaying a busy indicator so that user is acknowledged that file is uploading. Here we're just going to create a global variable that tells whether or not we're on the first load of the iFrame and the function to upload the image:
Now the form is setup to post to our iFrame. Here is the code which checks if any file exists against this record already it is displayed and validation is also applied in this event, actually this event will be called when iframe will load, on first pageload also iframe load event is called so thats why i have put validation code as well here, i am just checking by flag is it a first load of iframe or not, if it's not first load then it means user is uploading file and we may want to validate on file size, i am not allowing file greater than 4MB, you can also check here for file extension as well, may be you just want to allow user to upload images so in this function you can check that as well if you want to check on specific type of files to be just uploaded :
and at last here is the deleting of file using ajax:
and GetFiles actions looks like :
I have created a sample project to demonstrate it.The screen shot is attached:
You can download sample source code from here
Hope you will get the understanding how its working, for reference from where i got help you can also visit this link for more details: AJAX File Uploads with jQuery and MVC 3
Introduction:
In this post, you will learn how to upload file in asp.net mvc without reloading the whole page or wihtout refreshing the whole page instead we will update specific portion of page which is normally said aync postback in webforms terms.
Background:
Once upon a time i came across scenario where i needed to upload file in my asp.net mvc web application but its quite simple if we want full post-back, but i was thinking of doing it using Ajax by doing Partial Post-back.Using JQuery to post standard forms is extremely simple, but when posting multi-part forms for uploading files it's not so intuitive. This is due to browser security restrictions and sandboxing. Today I'm going to show you how to fake an asynchronous upload without reloading the page and get a result back from the server.
First Idea that came in my mind was using mvc helper Ajax.BeginForm and i came to know that it not supports file upload by partial post-back, it will do post-back.
Second Idea that was in my mind is to use JQuery and AJAX to post file on server so that i can avoid post-back, i tried my own way by posting file and returning partial view via AJAX but same issue was happening the partial view was rendering independently not on the same view, then i searched on internet and found a way.
This method uses IFrame and do a fake async postback and it looks like that file uplaoding via ajax, as our view remains same.
How It Works
In order to make the AJAX style file uploads work, we need to to post to
an iFrame. We're going to position an iFrame off the page to hide it
and then post to it. We'll return a partial view to the page and append it in some html container. It's important to note that due to security
limitations, the page being posted to must be on the same domain as the
page you're posting from. Also, this solution will be using a single hidden
iFrame, but for multiple simultaneous uploads you could dynamically
generate the iFrames and post to them.
Main View
Here is my view code i have created a form using mvc Helper and after the form i added an iframe in which our form will be posted:@{ ViewBag.Title = "Ajax File Uploading"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>Ajax File Uploading</h2> <div> <div> <h1>Upload File</h1> </div> @using (Html.BeginForm("Upload", "Home", FormMethod.Post, new { enctype = "multipart/form-data", id = "ImgForm", name = "ImgForm", target = "UploadTarget" })) { @* DataBase Record ID as Hidden Field against which we are uplaoding file *@ @Html.Hidden("ID", 65512) <div style="width: 100px; margin: auto; float: left; line-height: 30px; font-weight: bold;">File</div> <div style="width: 200px; float: left; margin: auto;"> <input type="file" id="uploadFile" name="file" data-val="true" data-val-required="please select a file" /> <span id="validityMessages" style="color: Red;"></span> </div> <div style="clear: both;"></div> <div style="width: auto; margin-top: 3%;"> <div style="margin: 5% 18%;"> <input id="upload" type="button" value="Upload" name="Upload" onclick="UploadImage()" /> <span id="uploadLoader" style="display: none;"> <img id="searchLoader" src="@Url.Content("~/Content/Images/loader.gif")" />Uploading Please Wait</span> </div> </div> } <div id="uploadsContainer"></div> <iframe id="UploadTarget" name="UploadTarget" onload="UploadImage_Complete();" style="position: absolute; left: -999em; top: -999em;"></iframe> </div> @section Scripts{ <script type="text/javascript"> $(document).ready(function () { $('img.delUpload').live('click', function () { var Id = this.id; var url = '@Url.Action("DeleteFile","Home")'; url = url + "?id=" + Id $.get(url, function (response) { $('#uploadsContainer').html(response); }); }); }); </script> <script type="text/javascript"> var isFirstLoad = true; function UploadImage() { // check for size of file not greater than 1MB if ($("#uploadFile").val()) { var iSize = ($("#uploadFile")[0].files[0].size / 1024); iSize = (Math.round((iSize / 1024) * 100) / 100); if (iSize > 4) { alert("File must be less than 4MB"); $('span#validityMessages').html("File must be less than 4MB"); return; } else { // on form post showing Busy Indicator $('#uploadLoader').show(); $("#ImgForm").submit(); // post form console.log(iSize + "Mb"); } } // check for no file selected for upload else { $('span#validityMessages').html("Please select a File of size less than 4MB!"); return; } } function UploadImage_Complete() { //Check to see if this is the first load of the iFrame if (isFirstLoad == true) { isFirstLoad = false; } $('#uploadLoader').hide(); //Reset the image form so the file won't get uploaded again document.getElementById("ImgForm").reset(); // this call will get uploads if any exists on server against this id and after successfull upload refreshing partial view to get the latest uploads GetFiles(); } function GetFiles() { var url = '@Url.Action("GetFiles","Home")'; var Id = $('input#ID').val(); url = url + "?id=" + Id $.get(url, function (response) { $('#uploadsContainer').html(response); }); } </script> }
Uploads ViewModel
Here is my uploads view model which is strongly typed with the uploads partial view:
public class UploadsViewModel { public long ID { get; set; } public ListUploads { get; set; } public UploadsViewModel() { this.Uploads = new List (); } } public class File { public string FilePath { get; set; } public long FileID { get; set; } public string FileName { get; set; } }
Upload Controller Method
Here is my controller method, i am taking two parameters input one is the posted form, in form i am posting some values in hidden fields required for DB updates and second parameter is posted file, i am simply saving the file in Uploads directory on server and updating a table in DB using Entity Framework and at the end returning partial view which will display the uploaded file name, thumbnail if its a image type file and button for deleting the file.For this post i am storing the state in Session:
[HttpPost] public ActionResult Upload(FormCollection form, HttpPostedFileBase file) { UploadsViewModel uploadsViewModel = Session["Uploads"] != null ? Session["Uploads"] as UploadsViewModel : new UploadsViewModel(); uploadsViewModel.ID = long.Parse(form["id"]); File upload = new File(); upload.FileID = uploadsViewModel.Uploads.Count + 1; upload.FileName = file.FileName; upload.FilePath = "~/Uploads/" + file.FileName; //if (file.ContentLength < 4048576) we can check file size before saving if we need to restrict file size or we can check it on client side as well //{ if (file != null) { file.SaveAs(Server.MapPath(upload.FilePath)); uploadsViewModel.Uploads.Add(upload); Session["Uploads"] = uploadsViewModel; } // Save FileName and Path to Database according to your business requirements //} return PartialView("~/Views/Shared/_UploadsPartial.cshtml", uploadsViewModel.Uploads); }
Uploads Partial View
Here is my partial view code:
@model List<AjaxFileUploading.ViewModels.File> @{ Layout = null; var IsAnyImage = Model.Any(x => new String[] { "jpg", "jpeg", "png", "gif" }.Contains(x.FileName.Split('.').Last())); } @if (Model.Any()) { <h3>Uploads</h3> <hr /> <table style="width: 500px;"> <tr> @if (IsAnyImage) { <th>Thumbnail</th> } <th>FileName</th> <th>Action</th> </tr> <tbody> @for(int i=0; i< Model.Count; i++) { <tr> <td style="text-align: center;"> <img width="60" src="@Url.Content(Model[i].FilePath)" /> </td> <td style="text-align: center;"> <a href="@Url.Content("~/Uploads/" + Model[i].FileName)" target="_blank">@Model[i].FileName</a></td> <td style="text-align: center;"> <img id="@Model[i].FileID" class="delUpload" width="20" src="@Url.Content("~/Content/Images/delete.png")" /></td> </tr> } </tbody> </table> } else { <h3>No Uploads Found!</h3> }
Javascript and Jquery for MainView
The first thing we need to do is include a version of jQuery. Since I'm
writing an MVC application, I've chosen to use bundles that are provided by the framework, so you will see follwing line written in master layout _Layout.cshtml which is located in Views >> Shared directory:
@Scripts.Render("~/bundles/jquery")
Now we can start creating our JavaScript to handle our form posts. The UploadImage() method isn't absolutely necessary since all our example does is post the form, however as in my case i am displaying a busy indicator so that user is acknowledged that file is uploading. Here we're just going to create a global variable that tells whether or not we're on the first load of the iFrame and the function to upload the image:
Now the form is setup to post to our iFrame. Here is the code which checks if any file exists against this record already it is displayed and validation is also applied in this event, actually this event will be called when iframe will load, on first pageload also iframe load event is called so thats why i have put validation code as well here, i am just checking by flag is it a first load of iframe or not, if it's not first load then it means user is uploading file and we may want to validate on file size, i am not allowing file greater than 4MB, you can also check here for file extension as well, may be you just want to allow user to upload images so in this function you can check that as well if you want to check on specific type of files to be just uploaded :
<script type="text/javascript"> var isFirstLoad = true; function UploadImage() { // check for size of file not greater than 1MB if ($("#uploadFile").val()) { var iSize = ($("#uploadFile")[0].files[0].size / 1024); iSize = (Math.round((iSize / 1024) * 100) / 100); if (iSize > 4) { alert("File must be less than 4MB"); $('span#validityMessages').html("File must be less than 4MB"); return; } else { // on form post showing Busy Indicator $('#uploadLoader').show(); console.log(iSize + "Mb"); } } // check for no file selected for upload else { $('span#validityMessages').html("Please select a File of size less than 4MB!"); return; } } function UploadImage_Complete() { //Check to see if this is the first load of the iFrame if (isFirstLoad == true) { isFirstLoad = false; } $('#uploadLoader').hide(); //Reset the image form so the file won't get uploaded again document.getElementById("ImgForm").reset(); // this call will get uploads if any exists on server against this id and after successfull upload refreshing partial view to get the latest uploads GetFiles(); } </script>
and at last here is the deleting of file using ajax:
$(document).ready(function () { $('img.delUpload').live('click', function () { var Id = this.id; var url = '@Url.Action("DeleteFile","Home")'; url = url + "?id=" + Id $.get(url, function (response) { $('#uploadsContainer').html(response); }); }); });
Delete File Controller Method
Here is the Delete File Action:
public ActionResult DeleteFile(long id) { /* Use input Id to delete the record from db logically by setting IsDeleted bit in your table or delete it physically whatever is suitable for you for DEMO purpose i am stroing it in Session */ UploadsViewModel viewModel = Session["Uploads"] as UploadsViewModel; File file = viewModel.Uploads.Single(x => x.FileID == id); try { System.IO.File.Delete(Server.MapPath(file.FilePath)); viewModel.Uploads.Remove(file); } catch (Exception) { return PartialView("~/Views/Shared/_UploadsPartial.cshtml", viewModel.Uploads); } return PartialView("~/Views/Shared/_UploadsPartial.cshtml", viewModel.Uploads); }
and GetFiles actions looks like :
public ActionResult GetFiles(long Id) { UploadsViewModel viewModel = Session["Uploads"] as UploadsViewModel; return PartialView("~/Views/Shared/_UploadsPartial.cshtml", (viewModel == null ? new UploadsViewModel().Uploads : viewModel.Uploads)); }
I have created a sample project to demonstrate it.The screen shot is attached:
You can download sample source code from here
Download Source Code (AjaxFileUploading.zip)
Hope you will get the understanding how its working, for reference from where i got help you can also visit this link for more details: AJAX File Uploads with jQuery and MVC 3
asp.net mvc 4 routing Urls
Sunday, August 04, 2013 by Ehsan Sajjad 2 Comments A+ a-
Hi guys,
This Saturday i got in to situation, scenario was when we pass parameters to an action when we are calling action as get not post data is passed to the action parameters but if we see our application url, the values is shown in the url as query string this way:
which looks very awkward as its not a good approach to show database related ids in the urls, its unsafe, user just needs to play with numbers and can access the records that we dont want to be accesssible to him, so i needed a way so that my url be shown like this:
or like this:
When i go-ogled i came to know that we can configure the mvc application routes in Global.asax file of our project which was very simple, but in my case it was not very simple, it was really weird and wasted my one day in achieving that i will explain why it happened. We can achieve the thing by writing this code for our particular Controller's action in Global.asax:
as in my case my Verification Controller's Index action was called and two parameters were paased to it and my url was looking like this:
and i dont't want the url to be like this, the solution is simple you have to write this code in Golobal.asax, i am writing the code for the above controller's action:
But in my case i was developing application asp.net mvc 4 and i noticed that asp.net mvc4 adds some classes to the App_Start folder automatically at project creation by defaul, i was adding routing code in the Global.asax file but in the Application_Start method this line was written which i did'nt noticed before :
As you see for registering routes in Application_Start method RegisterRoutes method is called of class RouteConfig which is available in the App_Start folder of Solution, this was the issue i was writing route code in Global.asax but in actual my that method was not calling method was called of class RouteConfig, when i realized i moved my route code to that class method, and things worked.
This Saturday i got in to situation, scenario was when we pass parameters to an action when we are calling action as get not post data is passed to the action parameters but if we see our application url, the values is shown in the url as query string this way:
http://localhost:34795/TestDetails?flag=4&sub=2
which looks very awkward as its not a good approach to show database related ids in the urls, its unsafe, user just needs to play with numbers and can access the records that we dont want to be accesssible to him, so i needed a way so that my url be shown like this:
http://localhost:34795/TestDetails/4/2
or like this:
http://localhost:34795/TestDetails
When i go-ogled i came to know that we can configure the mvc application routes in Global.asax file of our project which was very simple, but in my case it was not very simple, it was really weird and wasted my one day in achieving that i will explain why it happened. We can achieve the thing by writing this code for our particular Controller's action in Global.asax:
as in my case my Verification Controller's Index action was called and two parameters were paased to it and my url was looking like this:
http://localhost:34795/Verification?DepartmentID=3&SubDepartmentID=2
public class MvcApplication : System.Web.HttpApplication { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Verification", // Route name "Verification/{DepartmentID}/{SubDepartmentID}", // URL with parameters new { controller = "Verification", action = "Index", DepartmentID = UrlParameter.Optional, SubDepartmentID = UrlParameter.Optional } // Parameter defaults ); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); BundleTable.Bundles.RegisterTemplateBundles(); } }
But in my case i was developing application asp.net mvc 4 and i noticed that asp.net mvc4 adds some classes to the App_Start folder automatically at project creation by defaul, i was adding routing code in the Global.asax file but in the Application_Start method this line was written which i did'nt noticed before :
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); AuthConfig.RegisterAuth(); }
As you see for registering routes in Application_Start method RegisterRoutes method is called of class RouteConfig which is available in the App_Start folder of Solution, this was the issue i was writing route code in Global.asax but in actual my that method was not calling method was called of class RouteConfig, when i realized i moved my route code to that class method, and things worked.
Subscribe to:
Posts (Atom)
Translate Blog
Recommended Books
About
CodeProject MVP
CodeProject Profile
Popular Posts
-
Sometimes we have scenario where we need to make a DataTable from a List of some type.Normally what comes in mind is that create a DataTabl...
-
CodeProject Introduction: In this post, you will learn how to upload file in asp.net mvc without reloading the whole page or wihtout r...
-
Today i am writing about how to implement searchable dropdown list in asp.net mvc 4. Actually i came across a scenario where the drop down...
-
When we create Registration form on a web application, we have to check that the email address and username that user is entering is uniqu...
-
When using Enum sometime we dont want to show the enum name that we have in code to user but instead of that we want to show some te...
-
Today i am sharing another feature which Facebook and LinkedIn like paging, i came across a scenario to show doctors list in our database, ...
-
CodeProject Introduction I frequently come across questions on online forums like StackOverflow in which the questioners are able to c...
-
Question: I am getting this (dbexception unhandled by user code) {"An error occurred while updating the entries. See the inner ...
-
CodeProject Nowadays i am trying to learn different design patterns in object oriented paradigm that are pretty useful to implement generi...
-
Hi, today i am sharing that how you can implement something that needs to be executed before every action is called. In asp.net mvc every...