BlueLemonCode.Com

Yet Another Tech Blog

Upload and Download files in database using MVC & Linq to SQL

Introduction

In this short article, I am going to demonstrate how can you upload and download files into database using MVC and LINQ to SQL. This article assumes that you have basic understanding of Asp.net MVC.

Background

Asp.net MVC is around for quite some time now (already in version 4 at the time of this writing). Still many times i find this question being asked in forums. "How to upload and download files in MVC?" and that too using LINQ to SQL.

So, here is my attempt to explain the same. 

Article Body

In Asp.net web forms we had FileUpload server control also, The HttpPostedFile property of file upload control which gave very abstract cover to underlying file upload functionality making it not necessary for developers to know what is going in behind scene.

With Asp.net MVC we have to live without server controls. But that's not so difficult :)

Lets start by building a demo application in MVC.

Assuming you have created default MVC project, add link in master page for file upload use action name as FileUpload. Now, I am going to use HomController to add action method FileUpload but, you can create a separate controller class.

For this demo to work, its essential that we create a database table in sql database so, here is the structure of my table which has very basic fields. Your actual table may have lot more fields.

 

Lets now add LINQ to SQL class file (.dbml file) into project and name it as DBContext.dbml then, drag and drop the new created table from server explorer onto DBContext.dbml designer area and save.

Now, we have a action method and LINQ to SQL class file ready. The page we are going to design will have provision for both upload the file and download the files. To be able to download the files, We should first display the list of all available files. so, lets first add code in our action method to return list of files and download link

our action method will now look like this. It just refers LINQ to SQL class and converts the incoming data to List to assign to Model

public ActionResult FileUpload()
    {
        DBContext dataContext = new DBContext();
        var returnData = dataContext.FileDumps;
        ViewData.Model = returnData.ToList();
        return View();
    }

Now, lets create a view. Right click on the action method name and select add view. For this demo, I am not creating a strongly typed view but it is advisable. We now have to create a table like structure to display list of files and download link. As, we don't have "dear" gridview in MVC applications, we will have to build one of our own using table tags and looping through model data.

<div id="ShowList" title="File List">
        <table width="50%">
        <thead>
            <th>
                File ID
            </th>
            <th>
                File Name
            </th>
            <th>
                File Download
            </th>
        </thead>
        <tbody>
        <%
            foreach (var rec in ViewData.Model)
        {%>
           <tr>
            <td>
                <% =rec.ID %>
            </td>
            <td>
                <%= rec.FileName%>
            </td>
            <td>
                <%= Html.ActionLink("Download", "Filedownload", new { id=rec.ID})%>         
            </td>
           </tr>
        <%} %>
        </tbody>
        </table>
    </div>

Note that, Html.ActionLink method is referring to action method named "FileDownload" and passing ID as a parameter. We will create this action method later. Our basic page is now ready. so run the page to see "File Download" link in menu of of home page. The File Upload view will now display empty table with headers (since we have not yet uploaded any files in database)

Lets now create file upload functionality. File upload is always requires a post form method in case of asp.net web forms, server control used to take care of this requirement (If you remember from old classic Asp days, it was developers job) Also, form enctype attribute should be "multipart/form-data". the multipart enctype These two points are essential for file to be uploaded successfully.

Why enctype should be "multipart/form-data"

multipart/form-data represents form content type used to encode form data while submitting to server. The form data is posted in parts with specific boundary element. As per W3G specification , this content type should be used in case of posting file or binary data to server.

you can read more about explaination on W3G site here

Lets now add a form element with a input type file and a button (input type submit) I am going to add this piece of code at top of page before the html table which we just added.

<div id="UploadSection">        
    <% using (Html.BeginForm("fileUpload", "home", FormMethod.Post, new { enctype = "multipart/form-data" }))
    {%><br />
        <p><input type="file" name="fileUpload1" /> </p>
        <p><input type="submit" value="Upload file" /></p>        
    <% } %>    
</div>

Note that we have referred to FileUpload action method in form element. In absence of file upload server control, we will have to use underlying request object. The request object in MVC controller is derived from HttpRequestBase.Controller.

Hence, the Request.Files method in controller will give us the access to all files uploaded in the form post (that's where multipart/form-data enctype is used. as multiple files uploaded with message boundaries in between)

Now the modified code of fileupload action method is below.

public ActionResult FileUpload(HttpPostedFileBase Files)
   {
       //create object of LINQ to SQL class
       DBContext dataContext = new DBContext();
       //loop through request file collection 
       foreach (string upload in Request.Files)
       {
           //create byte array of size equal to file input stream
           byte[] fileData = new byte[Request.Files[upload].InputStream.Length];
           //add file input stream into byte array
           Request.Files[upload].InputStream.Read(fileData, 0, Convert.ToInt32(Request.Files[upload].InputStream.Length));
           //create system.data.linq object using byte array
           System.Data.Linq.Binary binaryFile = new System.Data.Linq.Binary(fileData);
           //initialise object of FileDump LINQ to sql class passing values to be inserted
           FileDump record = new FileDump { FileData = binaryFile, FileName = System.IO.Path.GetFileName(Request.Files[upload].FileName) };
           //call InsertOnsubmit method to pass new object to entity
           dataContext.FileDumps.InsertOnSubmit(record);
           //call submitChanges method to execute implement changes into database
           dataContext.SubmitChanges();
       }
       var returnData = dataContext.FileDumps;
       ViewData.Model = returnData.ToList();
       return View();
   }

The above code is fairly commented to understand what each line is meant for. After adding this code, run the page and try to upload the file. The file should get uploaded and saved to database at the same time, file information should get displayed in the table.

We now have everything working except that File Download link in table will not work (of course, we have not added the action method for that yet). In html code of table, we had used ActionLink and action method named "FileDownload". It is now time to create this method in controller class. The action method accepts a integer value ID based on which file is retrived from databas.

public FileContentResult FileDownload(int id)
   {
       //declare byte array to get file content from database and string to store file name
       byte[] fileData;
       string fileName;
       //create object of LINQ to SQL class
       DBContext dataContext = new DBContext();
       //using LINQ expression to get record from database for given id value
       var record = from p in dataContext.FileDumps
                    where p.ID == id
                    select p;
       //only one record will be returned from database as expression uses condtion on primary field
       //so get first record from returned values and retrive file content (binary) and filename 
       fileData = (byte[])record.First().FileData.ToArray();
       fileName = record.First().FileName;
       //return file and provide byte file content and file name
       return File(fileData, "text", fileName);
   }

Again, code for FileDownload action method is fairly commented to understand clearly. Now, run the page for final time to see upload file, download file in action and file getting stored in sql table.

Our final page after upload and download will look like this

Conclusion

Unlike Asp.net web forms, little tasks like file upload and download require lot of code and attention in MVC. However with clear understanding of concept and close attention it can be done very easily and the benefits in return are much better (MVV advantage).

In the next article we will look into some of the other MVC concepts. Meanwhile I would like to know the readers review :)

Thanks for visiting and Happy Coding!