Back in the ASP world, uploading files via a web page was a tricky problem. The problem was that due to the encryption type of the form used to submit the file from the client's browser, receiving such files on the server side was a complex task. Data had to be retrieved as a safe byte array and decrypted before it could be used. Most of the people resorted to 3rd party DLLs. A few wrote their own DLLs and some even wrote purely ASP solutions to this problem using VBScript. Fortunately for us, the .NET Framework SDK comes with a few classes that make uploading of the files simple from the web developer point of view. This article will demonstrate following concepts:
Setting Up an HTML Form for File Uploading
The requirements for an HTML form to be able to upload files are very simple: you have to use multipart/form-data
encryption and you have to use method of post
.
<form id="Form1" method="post" runat="server" enctype="multipart/form-data">
HTML input control that is used to upload file has a simple type of file
.
<input id="filMyFile" type="file" runat="server">
This is all that needs to be done to an HTML form for a client to be able to submit a file to your ASP.NET application.
<INPUT TYPE="file"
corresponds to the System.Web.UI.HtmlControls.HtmlInputFile
type of object on the server side. So if you, like myself, are using a CodeBehind
architecture for your page you will have a member field definition similar to this in your class:
protected HtmlInputFile filMyFile;
HtmlInputFile
classes have a few different properties, but the only one that really concerns us here is PostedFile
. This property will tell us all we need to know about the file that had been uploaded to our server. The PostedFile
property is null
if no file was submitted by a client. So, we can simply check whether a file was sent by doing something like this:
if( filMyFile.PostedFile != null )
{
}
else
{
}
The PostedFile
property will contain a valid System.Web.HttpPostedFile
object if file indeed was uploaded. HttpPostedFile
provides us with 4 properties:
ContentLength
: size of uploaded file in bytes
ContentType
: MIME type of uploaded file, i.e. "image/gif"
FileName
: full path to uploaded file on client's system, i.e. c:\Some folder\MyPicture.gif
InputStream
: stream object that gives us access to uploaded data
Let's find out the size of the file:
HttpPostedFile myFile = filMyFile.PostedFile;
int nFileLen = myFile.ContentLength;
Now that we know the size, we can move on to retrieving the data. First, we need to allocate a byte array to store the data:
byte[] myData = new byte[nFileLen];
Next, we can read the uploaded file in our buffer using the InputStream
object:
myFile.InputStream.Read(myData, 0, nFileLen);
At this point, we have successfully retrieved the uploaded file into a byte array called myData
. What to do with it next depends largely on the requirements of your application. I will show both saving the file to hard drive and to a database in the next sections.
I wrote a simple function for my demo project that stores files to a disk:
private void WriteToFile(string strPath, ref byte[] Buffer)
{
FileStream newFile = new FileStream(strPath, FileMode.Create);
newFile.Write(Buffer, 0, Buffer.Length);
newFile.Close();
}
I pass a full path of where I want a file to be stored and reference to a buffer with file data to this function. It uses the System.IO.FileStream
object to write a buffer to a disk. This very simplistic 3 lines of code approach will work in most cases. A couple of considerations here are: getting the filename of the uploaded file and the security. Since the FileName
property of PostedFile
is a full path to the uploaded file on a client's computer, we will probably want to use only the filename portion of that path. Instead of using some parsing techniques to look for backslashes and things like that, we can use a very convenient little utility class: System.IO.Path
.
string strFilename = Path.GetFileName(myFile.FileName);
Security is another matter. In my demo project, I store files in the same folder where the project is executed. In order for that to work, the ASPNET account (the account that is used to execute ASP.NET processes) has to have Write permissions on that folder. By default it does not, so you need to right-click the folder, go to the security tab and add the ASP.NET account to the list. Then grant write permissions to that account by checking the Write checkbox and click OK.
The following function is used in my demo project to store uploaded files in the database:
private int WriteToDB(string strName, string strType, ref byte[] Buffer)
{
int nFileID = 0;
OleDbConnection dbConn = new OleDbConnection(GetConnectionString());
OleDbDataAdapter dbAdapt = new OleDbDataAdapter("SELECT * FROM tblFile", dbConn);
dbAdapt.MissingSchemaAction = MissingSchemaAction.AddWithKey;
OleDbCommandBuilder dbCB = new OleDbCommandBuilder(dbAdapt);
dbConn.Open();
DataSet dbSet = new DataSet();
dbAdapt.Fill(dbSet, "tblFile");
DataTable dbTable = dbSet.Tables["tblFile"];
DataRow dbRow = dbTable.NewRow();
dbRow["FileName"] = strName;
dbRow["FileSize"] = Buffer.Length;
dbRow["ContentType"] = strType;
dbRow["FileData"] = Buffer;
dbTable.Rows.Add(dbRow);
dbAdapt.Update(dbSet, "tblFile");
if( !dbRow.IsNull("FileID") )
nFileID = (int)dbRow["FileID"];
dbConn.Close();
return nFileID;
}
The Access database that I used in the demo project has only one table tblFile
that has 5 fields defined as follows:
FileID � autonumber Filename � text 255 FileSize - long integer ContentType � text 100 FileData � OLE Object
The code in the above function is very straightforward. I create OleDbConnection
, OleDbDataAdapter
and DataSet
objects and then append a row to the only table in DataSet
. The important thing to note here is that the OleDbCommandBuilder
object needs to be created and initialized with a reference to the OleDbDataAdapter
object to build the insert query for us automatically. Also, if you would like to retrieve the newly assigned FileID of the file you have just stored in the database, you need to make sure you set the MissingSchemaAction
property of Adapter to MissingSchemaAction.AddWithKey
. That assures that the Primary Key/autonumber FileID field will be populated with the new ID when you call the Update
method of your DataAdapter
.
The local ASP.NET account has to have Write permissions to a database file in order for your code to succeed. If your file is placed anywhere under wwwroot
, you will need to manually give the database file all the necessary permissions before running my demo project.
I used the same ASPX page to read and return file data out of the database in my demo project. I check for the FileID parameter being passed to a page. If it was passed, I know that I need to return a file rather than process the user's input:
private void Page_Load(object sender, System.EventArgs e)
{
if( Request.QueryString["FileID"] != null )
{
ShowTheFile(Convert.ToInt32(Request.QueryString["FileID"]));
}
}
The ShowTheFile
function does all the work here:
private void ShowTheFile(int FileID)
{
string SQL = "SELECT FileSize, FileData, ContentType FROM tblFile WHERE FileID = "
+ FileID.ToString();
OleDbConnection dbConn = new OleDbConnection(GetConnectionString());
OleDbCommand dbComm = new OleDbCommand(SQL, dbConn);
dbConn.Open();
OleDbDataReader dbRead = dbComm.ExecuteReader();
dbRead.Read();
Response.Clear();
Response.ContentType = (string)dbRead["ContentType"];
Response.OutputStream.Write((byte[])dbRead["FileData"], 0, (int)dbRead["FileSize"]);
dbConn.Close();
Response.End();
}
I am using OleDbConnection
, OleDbCommand
and OleDbDataReader
objects to retrieve data. The next step is to clear the Response
output buffer to make sure that no other information is being sent to the client besides our file data. That will only work if buffering of your pages is turned on (default in ASP.NET). I set the ContentType
property of the Response
object to the content type of our file. Then I write file data into the Output stream of the Response
object. The important thing here is to call Response.End()
at the end to prevent farther processing of this page. We are done here and we do not want for other code to execute.
Important:
Because my demo project writes files to disk or data to an Access database, you need to make sure that security permissions are set properly on folders and files where you want to write. Beta versions of .NET used the System
account to execute ASP.NET processes and it had access to everything on your computer. However, the final release of .NET runs all ASP.NET processes under a local ASP.NET account. That account needs to have Write permissions to all the folders and files that are being used to write data.
In order to make my demo project work under the .NET release version, you need to place it in a folder (anywhere under wwwroot
) and set the permissions of that folder as follows: Right-Click the folder, go to Properties, Security tab, make sure the local ASPNET account has Read and Write permissions on that folder. If not, Add local ASPNET account to the list and give it read and write permissions. Click OK.