Save And Retrieve Files From a Sql Server CE Database with WebMatrix

A perennial choice that web developers have to make is whether to store uploaded files to the file system or a database. Each method has its pros and cons, and the debate over which approach is best continues to rage. This article will not add to the noise. It will simply demonstrate how to upload files to the Sql Server CE database which is the default option in Web Pages, and how to retrieve them for users again.

First, we need a database table with the right kind of data types for storing various information about the uploaded file. At a minimum, we need the the file name, the MIME type and the actual binary data.

The file name and MIME types are both strings, so nvarchar will do for those. The MIME type describes the type of content contained in the file, so a text file would have a MIME type of text/plain. An html file will have a MIME type of text/html. Images generally have an image/ prefix to their MIME type - image/jpg, image/gif etc. Files specific to certain applications have application/ as their prefix: application/vnd.ms-excel or application/vnd.ms-powerpoint. A catch-all MIME type of application/octet-stream covers many other file types, and just tells the browser "this is binary data of some kind". the MIME type is also known as the content-type. The best data type in SQL Server CE for binary data is the image data type. Note - it's not just image files that can stored here, it's any kind of binary data.

You will need to add the FileUpload helper to your site. This is available from the Package Manager as part of ASP.NET Web Pages Helpers.

Add a new page and call it upload.cshtml. This page will contain the file upload so that users can get their files to the web server. It will also save those files to the database, and in the case of an image, display the result, or with other file types, it will display a link to the newly saved file. There is a helper method for creating file uploads, FileUpload.GetHtml():

@FileUpload.GetHtml(
    initialNumberOfFiles:1,
    allowMoreFilesToBeAdded:false,
    includeFormTag:true,
    uploadText:"Upload")

The helper lets you specify the number of initial uploads you want to display, whether you would like to allow the user to dynamically create more of them, whether you want the whole thing wrapped in form tags and what you would like as the text on the second button. You cannot change the text on the Browse button. If you are displaying the upload as part of an existing form, you should set includeFormTag to false. Since it is on its own, I am setting it to true so that the form tags render. At the moment, when you run the page, you are presented with the file upload, and it works. However, nothing happens with the uploaded file. Some code is needed.

When working with files, you will generally need to reference System.IO as that is the set of classes that has methods for working specifically with file objects. That needs to be referenced at the top of the page. If files are uploaded, they can be accessed via the Request.Files collection, which has a zero-based index. If one file is uploaded, it will be available in Request.Files[0]. If files multiple are available, the first is at Request.Files[0], the second at Request.Files[1] and so on. Request.Files has a Count property, which returns the actual number of files. In this example, we only need to worry about one file, so Request.Files[0] will get it for us, but only if the form has been submitted. Of course it is possible that someone submits the form without selecting a file. We will test for that condition too.

If there is a file there, we need three pieces of data - it's name, it's type (MIME) and the actual binary data that constitutes the file. That all gets added to the database. Here's the full code for the top of upload.cshtml:

@{ 
    int id = 0;
    var fileName = "";
    var fileMime = "";
    if (IsPost) {
        var uploadedFile = Request.Files[0];
        fileName = Path.GetFileName(uploadedFile.FileName);
        if(fileName != String.Empty)
        {
            fileMime = uploadedFile.ContentType;
            var fileStream = uploadedFile.InputStream;
            var fileLength = uploadedFile.ContentLength;
            
            byte[] fileContent = new byte[fileLength];
            fileStream.Read(fileContent, 0, fileLength);
            var db = Database.Open("FileUploading");
            var sql = "INSERT INTO Files (FileName, FileContent, MimeType) VALUES (@0,@1,@2)";
            db.Execute(sql, fileName, fileContent, fileMime);
            id = (int)db.GetLastInsertId();
        }
    }
}

The final line of code gets the identity value of the most recently added row in the database and applies it to the id variable. That's used much further down the page where this code appears:

@if (IsPost && fileName != String.Empty) {
    <span>File uploaded!</span><br/>
    if(fileMime.StartsWith("image")){
        <img src="Download.cshtml?Id=@id" alt="" />
    }
    else {
        <a href="Download.cshtml?Id=@id">Click Here</a>
    }
}  

This code, which goes below the file upload code, checks to see if the form has been posted back, and if there was a file. If it has and there is, it will look at the content type, and if it's an image, write an img element to the browser, with the src attribute pointing to another file (download.cshtml) and passing in the new id as a querystring value. This means that the newly added image is displayed. If the file isn't an image, the code writes a hyperlink to the page, again pointing the download.cshtml with a querystring value. Clicking the link will result in the file being downloaded. We'll look at the code in download.cshtml next:

@{
    int id = 0;
    if(Request["Id"].IsInt()){
        id = Request["Id"].AsInt();
        var db = Database.Open("FileUploading");
        var sql = "Select * From Files Where FileId = @0";
        var file = db.QuerySingle(sql, id);
        if(file.MimeType.StartsWith("image/")){
            Response.AddHeader("content-disposition", "inline; filename=" + file.FileName);
        } else {
            Response.AddHeader("content-disposition", "attachment; filename=" + file.FileName);
        }
        Response.ContentType = file.MimeType;
        Response.BinaryWrite((byte[])file.FileContent);
    }
}

When this file is requested, if there is a valid value in the query string, the matching file data is obtained from the database. A Response header is added. If the file is an image, the content-disposition value is set to inline, otherwise it is set to attachment. Then the binary data is written to the Response, resulting in the file being delivered to the user.

A download which includes the code for both files and the database is available here.

 

Date Posted:
Last Updated:
Posted by:
Total Views to date: 30269

10 Comments

- dotnetcoder

Thank Mike! Very easy to understand. Please keep 'em coming, they save so much time for me.

- raf

very nice! thanks

- Mclazarini

Thanks. Very usefull.

- Marcello

Nice.
Question. Can I use a mySql database to do the same thing?
Unfortunately my provider does not support SQLCE.

- Mike

@Marcello,

Yes you can use MySQL to store binary data. I am not familiar with MySQL but I would have thought the code required to do so is almost identical to the article above.

- Marcello

Thanks Mike. And much compliments for all these really really useful articles, they're helping me a lot in developing applications.

- Alistair Davison

As always your articles always work best, Thanks. This may be an obvious question but how do you increase the allowance for bigger file sizes

Thanks

- Mike

@Alistair,

You do that in web.config via the httpRuntime maxRequestLength setting: http://msdn.microsoft.com/en-us/library/e1f13641(v=vs.100).aspx

- Praveen

I am new person and learning ASP.Net its use full for me

- Andrea

Please excuse my noob question. I am using your tips to add file upload to a form I have created.
I run into a compile error:
CS1061: 'System.Web.HttpRequestBase' does not contain a definition for 'File'....

I suspect this is related to your statement:
You will generally need to reference System.IO

How do I reference System.IO on my .cshtml?

Recent Comments

Mike 6/29/2015 2:22 AM
In response to MVC 5 with EF 6 in Visual Basic - Sorting, Filtering and Paging
This is the first example that I have found that works....

Marty 6/28/2015 4:57 AM
In response to Posting Data With jQuery AJAX In ASP.NET Razor Web Pages
Mike, what if I don't want to render back the text to the browser, but I want to send it some other...

Mike 6/27/2015 4:00 PM
In response to Migrating Classic ASP To ASP.NET Razor Web Pages Part One- Razor Syntax And Visual Basic
have you used any of the code converters to convert classic asp to c#? If so, which one do you have...

deepan 6/27/2015 12:49 PM
In response to How To Send Email In ASP.NET MVC
Your article is very very useful for me...thanks for giving this article ...this is very simple to u...

Howie 6/27/2015 6:20 AM
In response to Scheduled Tasks In ASP.NET With Quartz.Net
Mike - Great article. I'm curious about the lifetime of the scheduler - I see your is static and you...

Chris 6/26/2015 7:42 PM
In response to Ajax with Classic ASP using jQuery
I have searched the web high and low for a clean simple solution using JQuery, JSON, and Classic ASP...

aaron 6/26/2015 12:01 PM
In response to 7 C# 6.0 Features That Every ASP.NET Developer Should Know About
#6 enables a less-verbose functional programming style...

Marty 6/26/2015 5:26 AM
In response to Posting Data With jQuery AJAX In ASP.NET Razor Web Pages
Great article. You don't show the code in Receiver.cshtml to send data back. How would you send text...

Vimal 6/25/2015 11:09 AM
In response to iTextSharp - Working with Fonts
I want to edit my existing PDF. I want to change font color. I want to change is gray or some I to...

yoyo 6/24/2015 9:00 PM
In response to Dependency Injection and Inversion of Control with ASP.NET MVC
Hi, thanks for your explainations. Lionel...