AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1201&pId=-1
Building an Online Storage System in ASP.NET 2.0
page
by Xianzhong Zhu
Feedback
Average Rating: 
Views (Total / Last 10 Days): 31051/ 53

Introduction

Nowadays, many IT companies have provided their different kinds of online storage systems, which are very popular network services, to enable users to directly manage their data via a simple browser anywhere in the world. In this article I will try to dissect the key techniques in building such a system with a wholly ASP.NET 2.0-based sample.

Requirements and Goals

An online storage system, also called a web disk, can store your files securely in one location for easy access from any Internet-connected computer. It is easy to upload and retrieve files. The sample project called WebDisk in this article will achieve the goals listed below.

·         Browse the web disk

·         Upload files

·         Search files

·         Create folders

·         Rename folders

·         Delete files and folders

·         Rename files

·         Move files and folders

·         Examine file attributes

This seems to be a very useful and fun project! Let us now figure out how we are going to accomplish all that we have set out for ourselves.

Database Design

With the general goals introduced, we can now shift our attention to the database design.

Note that here I assumes that you are familiar with SQL Server 2005 Express Edition and how to create basic SQL clauses. To follow along with the examples, you had better download the source files accompanying this article.

In this article we will design a database named WebDiskDB, which contains two tables—Directory for file folders and Url for file attributes, respectively.

Now, let us look at the table structures for table Directory and Url in Table 1 and Table 2, respectively.

Table 1: Structure for table Directory

Field name

Type

Notes

DirID

int

Primary Key

Name

varchar(50)

The name of the file/folder

ParentID

int

ID of the parent folder

Contain

int

The whole size of the file/folder

FileCount

int

The total file number it contains(0 - if it's a file)

DirCount

int

The total folder number it contains(0 - if it's a file)

Flag

bit

Marking whether it is a file or a folder

CreateDate

datetime

Creating time

 

Table 2: Structure for table Url

Field name

Type

Notes

UrlID

int

ID(Primary Key)

Url

varchar(255)

linking address

Type

varchar(200)

type

DirID

int

The corresponding DirID in table Directory(foreign key)

VisitDate

datetime

Creating time

 

Visualizing the finish line

Creating WebDisk, we first should know what we are building; so what follows are some screenshots and descriptions of the WebDisk sample. The application essentially consists of three crucial screens: the main page screen, uploading file screen, and the searching screen.

Browsing the online storage system

Figure 1 shows the main page screen, Default.aspx. Here we grab file-related information from the client site and persist it into the database, WebDiskDB, on the server side.

Figure 1: The run-time view of the main page Default.aspx

This page includes a GridView control named DiskView for displaying the hark disk information, two DropdownList controls named DirList and MoveDirList for showing folder information, and two Button controls named ReturnBtn for returning to the parent folder and MoveBtn for moving files or folders.

Also, we add two hyperlinks, namely New folder for linking to page NewFolder.aspx and Search files for linking to page SearchFile.aspx.

In fact, using an online storage system is very much like operating a local file system except for the things associated with network connection and database persistence. From the screenshot in Figure 1 you may find this.

Uploading files

The next screen, uploading file screen, is shown in Figure 2. On this screen we should fulfill two different functionsuploading a single file to the server and uploading multiple ones. From the screenshot we can see that this page is pretty easy, on which there are a file uploading control named File , two buddy Button controls—addFile and SureBtn, and a simple button control, ReturnBtn. Here we use control File to browse and select the files to upload, while we use control addFile to add a new FileUpload control for multiple files uploading, and finally use button SureBtn to perform the uploading task.

Figure 2: The run-time view of page UploadFile.aspx

Note that when you click the button Upload file on the main page Default.aspx it will lead you to this page. And also, we should notice that here we browse files to upload by using a HTML INPUT control with type="file" rather than the ASP.NET server-side control FileUpload for the easier JavaScript programming in the client-side. Now let us focus on uploading. This is done when the user clicks the Upload all files button, which will trigger the event handler SureBtn_Click(object sender, EventArgs e) inside which function UploadFiles servers to upload the files onto the hard disk on the server and persist them into the database. Below we enumerate the detailed steps for function UploadFiles() to achieve uploading:

1.    Get all the files to upload from the current Request object of HttpContext and save them into a collection fileList.

2.    Obtain each file inside the collection and attach it to a HttpPostedFile object.

3.    Acquire the corresponding file name and save it onto the hard disk of the server site.

4.    Persist the gathered information of the uploaded files into the database.

5.    Exhibit details for the uploaded files on the page.

Please see the downloadable source code at the end of the article for detailed programming.

Searching files

Lastly, we come to the third important part of the application: the Searching files screen is also a typical one (see Figure 3).

Figure 3: The run-time view of page SearchFile.aspx

Just as nearly all the web pages in this application, this screen is also laid out using tables. There are more controls on this page. Let us enumerate their functions.

·         GridView FileViewfor listing the detailed info of the matched files

·         DropdownList DirListfor displaying  directory structure info

·         RequiredFieldValidator rfNfor checking whether the key words entered by the user are empty

·         TextBox Namefor entering the key words used to search for files

·         Button ReturnBtnfor navigating to the main page Default.aspx

·         Button SearchBtnfor performing the searching work

Now that we have an overview the whole online storage system and its main functions, let us make further detailed research into each part of the solution!

Dissecting the solution

We will start the dissection by looking at the Interface design for this application.

Interface design

The main component in this sample online storage system is an interfaceIDisk which declares nearly all the underlying methods associated with folders and files, such as method GetAllDirectoryFile() for getting information of sub folders and files, method GetFiles() for obtaining all the related files. The following gives us the class diagram of interface IDisk.

Figure 4

As shown in Figure 4, we encapsulate all the basic file and folder operation-related declarations into a single interface, which is a good practice in real life. Since all the modules are small and basic, and even self-explanatory, we do not dwell too much on them here; we will clarify some of them in the next section and in later use.

Data tier design

The data access tier of the whole system mainly relies on one class, Disk, which is derived from the interface IDisk depicted above and should implement all the methods declared in it. The sketch in Figure 5 gives us a more intuitive depiction of the relationships between the modules we will use.

Figure 5

Besides all the methods in interface IDisk, class Disk defines its own two methodsShowDirectory(DropDownList dirList, int nParent) and CreateChildNode(DropDownList dirList, DataTable dataTable, int nParent, string sParentName). The two methods are cooperatively responsible for displaying folder information according to the corresponding folder hierarchy. To achieve this goal, we take the steps as follows.

1.    Obtain folders data from inside the database and store up the results via a object dataTable.

2.    Append the root folder info into the DropDownList dirList.

3.    Retrieve all the sub folder info whose folder ID is nParent and add it into the control dirList by invoking method CreateChildNode.

4.    Repeat step 3 until all the related sub folders are added into the control dirList.

In the above flow, steps 1-3 mainly depend on method ShowDirectory, while step 4 is accomplished through method CreateChildNode. Below are their corresponding codes.

Listing 1: Code for the two own functions of class Disk

public void ShowDirectory(DropDownList dirList,int nParentID)
      {
            DataTable dataTable = SystemTools.ConvertDataReaderToDataTable(GetDirectorys());
            dirList.Items.Clear();     ///clear all the nodes
 
            DataRow[] rowList = dataTable.Select("ParentID='-1'");
            if(rowList.Length <= 0) return;
 
            ///Create and add a root node
            dirList.Items.Add(new ListItem("/",rowList[0]["DirID"].ToString()));
 
            ///Create other nodes
            CreateChildNode(dirList,dataTable,Int32.Parse(rowList[0]["DirID"].ToString()),"/");
      }
 
private void CreateChildNode(DropDownList dirList,
DataTable dataTable,int nParentID,string sParentName)
      {           
            ///when selecting data, we use sort expression-"OrderBy"
            DataRow[] rowList =
 dataTable.Select("ParentID='" + nParentID.ToString() +
 "'","CreateDate DESC");
            foreach(DataRow row in rowList)
            {
                  string sName = sParentName + row["Name"].ToString() + "/";
                  ///create a new node
                  dirList.Items.Add(new ListItem(sName,row["DirID"].ToString()));
 
            ///Call this function recursively to create other nodes
                  CreateChildNode(dirList,dataTable,Int32.Parse(row["DirID"].ToString()),sName);
            }
      }

Here, for brevity, we chose to omit all other methods for there are so many of them (see the downloaded source code for detail). Nevertheless, there are some interesting points inside method AddFile. This method will add a new file into the database using a stored procedure called Pr_AddFile which will use parametersName, Parent, Contain, Url and Type that represent file name, the parent directory ID, the size, the linking address, and its type.

Listing 2: Code for function AddFile

      public int AddFile(string sName,int nParentID,int nContain,string sUrl,string sType)
      {
            ///create the connection
            SqlConnection myConnection = new SqlConnection(
                  ConfigurationManager.ConnectionStrings["SQLCONNECTIONSTRING"].ConnectionString);
 
            SqlCommand myCommand = new SqlCommand("Pr_AddFile",myConnection);
            myCommand.CommandType = CommandType.StoredProcedure;
            ///add parameters for the stored procedure
            SqlParameter pName = new SqlParameter("@Name",SqlDbType.VarChar,200);
            pName.Value = sName;
            myCommand.Parameters.Add(pName);          
            SqlParameter pParentID = new SqlParameter("@ParentID",SqlDbType.Int,4);
            pParentID.Value = nParentID;
            myCommand.Parameters.Add(pParentID);            
            SqlParameter pContain = new SqlParameter("@Contain",SqlDbType.Int,4);
            pContain.Value = nContain;
            myCommand.Parameters.Add(pContain);
            SqlParameter pUrl = new SqlParameter("@Url",SqlDbType.VarChar,255);
            pUrl.Value = sUrl;
            myCommand.Parameters.Add(pUrl);
            SqlParameter pType = new SqlParameter("@Type",SqlDbType.VarChar,200);
            pType.Value = sType;
            myCommand.Parameters.Add(pType);
 
            ///define the returned value
            int nResult = -1;
 
            try
            {
                  ///open the connection
                  myConnection.Open();
                  ///execute the SQL clause
                  nResult = myCommand.ExecuteNonQuery();                
            }
            catch(SqlException ex)
            {
                  ///throw exception
                  throw new Exception(ex.Message,ex);
            }
            finally
            {   ///close the connection
                  myConnection.Close();
            }
            ///return nResult
            return nResult;
      }

From above, we can easily see that calling stored procedure Pr_AddFile will result in returning the ID of the new record, which is just what we expect. Here, we see the typical database operation. And since it is a fairly straightforward function and self-explained, we will not examine it in detail any more.

Creating folders

Here comes the page AddFolder.aspx on which we accomplish the task of creating folders. To begin, let us look at its page layout, as shown in Figure 5.

Figure 5: Design-time view of page AddFolder.aspx

Yes, it is a simple but typical interface structure with merely several common controls on the page! Here we also enumerate their functions.

·         DropdownList DirListfor displaying  directory structure info

·         TextBox Namefor user entering the name of the folder

·         RequiredFieldValidator rfNfor checking whether the directory info is empty

·         Button ReturnBtnfor navigating to page Default.aspx

·         Button AddBtnfor creating a new folder

When initialized, page AddFolder.aspx will show directory info in the DropdownList DirList, which is accomplished by calling function BindDirectoryData from inside the event handler Page_Load(object sender, EventArgs e).

Listing 3

    protected void Page_Load(object sender, EventArgs e)
    {
            if(!Page.IsPostBack)
            {   ///display directoy info
                  BindDirectoryData();
            }
    }
      private void BindDirectoryData()
      {   ///display directory info
            Disk disk = new Disk();
            disk.ShowDirectory(DirList,-1);
      }

Clicking the button OK will trigger the event AddBtn_Click(object sender, EventArgs e) which will create a new folder. Inside this event function AddDirecty is called to add the newly-created folder into the underlying database.

When we click the button Return, we are navigated back to page Default.aspx. Listing 12 is the corresponding code for the event ReturnBtn_Click(object sender, EventArgs e).

Listing 4

      protected void AddBtn_Click(object sender,EventArgs e)
      {
            try
            {   ///define a Disk object
                  IDisk disk = new Disk();
                  ///execute the database operation
                  disk.AddDirectory(Name.Text.Trim(),Int32.Parse(DirList.SelectedValue));
                  Response.Write("<script>alert('" + 
"You've succeded in adding the info,safekeep you data!');</script>");
            }
            catch(Exception ex)
            {   ///jump to the exception handling page
                  Response.Redirect("ErrorPage.aspx?ErrorMsg=" +
 ex.Message.Replace("<br>","").Replace("\n","")
                        + "&ErrorUrl=" +
 Request.Url.ToString().Replace("<br>","").Replace("\n",""));
            }
      }

Renaming

In this sample, renaming the file and renaming the folder are achieved in different web pages, EditFolder.aspx (see Figure 6) and EditFile.aspx (see Figure 7).

Figure 6: Design-time view of page EditFolder.aspx

Figure 7: Design-time view of page EditFile.aspx

Seeing the screenshots above, some readers may ask, "Why not put the two together?" The reason is that when we design interface IDisk, we define two independent functions, BindDirectoryData and BindFileData, to gain loose coupling. Since the two parts have nearly the same logic, let us merely concentrate on the code snippet associated with renaming the folder in Listing 11.

Listing 5: the key code in relation to renaming the folder

      int nDirID = 2;
    protected void Page_Load(object sender, EventArgs e)
    {
            ///obtain the value of parameter DirID
            if(Request.Params["DirID"] != null)
            {
                  if(Int32.TryParse(Request.Params["DirID"].ToString(),out nDirID) == false)
                  {
                        return;
                  }
            }
            if(!Page.IsPostBack)
            {   ///display the name of the directory
                  if(nDirID > -1)
                  {
                        BindDirectoryData(nDirID);
                  }
            }
    }
 
      private void BindDirectoryData(int nDirID)
      {
            IDisk disk = new Disk();
            SqlDataReader dr = disk.GetSingleDirectory(nDirID);
            if(dr.Read())
            {
                  Name.Text = dr["Name"].ToString();
            }
            dr.Close();
      }
      protected void EditBtn_Click(object sender,EventArgs e)
      {
            try
            {   ///define the object
                  IDisk disk = new Disk();
                  ///fulfill the database operation
                  disk.EditDirectory(nDirID,Name.Text.Trim());
                  Response.Write("<script>alert('" + 
"Modifying sucessfully,safekeep you data! " + "');</script>");
            }
            catch(Exception ex)
            {   ///jump to exception handling page
                  Response.Redirect("ErrorPage.aspx?ErrorMsg=" + 
ex.Message.Replace("<br>","").Replace("\n","")
                        + "&ErrorUrl=" +
 Request.Url.ToString().Replace("<br>","").Replace("\n",""));
            }
      }

Here, function BindDirectoryData(int nDirID) get info of the specified folder from the database using parameter nDirID and then display the folder's name in the TextBox Name. When we click the button OK, the event EditBtn_Click is triggered to call function EditDirectory to rename the folder and persist the new name in the database. As far as renaming a file is concerned, please see the downloadable source code accompanying this article.

Deleting

Deleting files/folders is also accomplished inside page Default.aspx. To do so, you can just click the button X and then the selected object will be deleted.

When we use Windows Explorer.exe to delete something, a dialog is always appearing to remind us of the possible danger. To also achieve such an effect, we add the related code into the RowDataBound event of the control DiskView (see Listing 6).

Listing 6

      protected void DiskView_RowDataBound(object sender,GridViewRowEventArgs e)
      {
            ImageButton deleteBtn = (ImageButton)e.Row.FindControl("DeleteBtn");
            if(deleteBtn != null)
            {
                  deleteBtn.Attributes.Add("onclick",
"return confirm('Are you sure to delete all the selected items?');");
            }
      }

Here, if the button deleteBtn is not null, then we attach a reminding dialog to it.

The main task to make the deletion is done within the GridView's RowCommand event.

Listing 7

      protected void DiskView_RowCommand(object sender,GridViewCommandEventArgs e)
      {
            if(e.CommandName == "delete")
            {
                  try
                  {   ///delete data
                        IDisk disk = new Disk();
                        disk.DeleteFile(Int32.Parse(e.CommandArgument.ToString()));
 
                        ///rebind the controls' data
                        BindDirectoryData(Int32.Parse(DirList.SelectedValue));
                Response.Write("<script>alert('" + 
"Deleting successfully,safekeep your data!" + "');</script>");
                  }
                  catch(Exception ex)
                  {   ///jump to the page dealing with exception handling
                        Response.Redirect("ErrorPage.aspx?ErrorMsg=" +
 ex.Message.Replace("<br>","").Replace("\n","")
                              + "&ErrorUrl=" + 
Request.Url.ToString().Replace("<br>","").Replace("\n",""));
                  }
            }
      }

Here, event RowCommand obtains the value of the property CommandName of the button X and save it in parameter CommandName. Next, we get the value of the property CommandArgument and convert it into an integer which is stored in the parameter nDirID. If all the requirements are satisfied, then we start to delete the selected object (file or folder) which is fulfilled by calling the function DeleteFile(int nDirID) of interface IDisk, removing the item (whose value of DirID is nDirID) from the database. Finally, an appropriate dialog appears to indicate whether the operation is successful or not.

Moving the files/folders

Moving files or folders is also accomplished through page Default.aspx. When clicking the button MoveTo, you can move the selected files or folders to another path. To do this, we will follow the steps listed below.

1.    Select the files/folders to move.

2.    Select the destination folder.

3.    Click button MoveTo to finish the task.

All the moving functions are fulfilled inside function MoveBtn_Click(object sender, EventArgs e), whose total code is shown below.

Listing 8

      protected void MoveBtn_Click(object sender,EventArgs e)
      {
            try
            {   ///define a Disk object
                  IDisk disk = new Disk();
                  foreach(GridViewRow row in DiskView.Rows)
                  {
                        CheckBox dirCheck = (CheckBox)row.FindControl("DirCheck");
                        if(dirCheck != null)
                        {
                              if(dirCheck.Checked == true)
                              {
                                    ///fulfill database operation
                                    disk.MoveDirectory(Int32.Parse(
DiskView.DataKeys[row.RowIndex].Value.ToString()),
                                          Int32.Parse(MoveDirList.SelectedValue));
                              }
                        }
                  }
                  ///rebind the data of the controls
                  BindDirectoryData(Int32.Parse(DirList.SelectedValue));
                  Response.Write("<script>alert('" +
 "Modifying successfully,safekeep your data!" + "');</script>");
            }
            catch(Exception ex)
            {   ///jump to the exception handling page
                  Response.Redirect("ErrorPage.aspx?ErrorMsg=" +
 ex.Message.Replace("<br>","").Replace("\n","")
                        + "&ErrorUrl=" + 
Request.Url.ToString().Replace("<br>","").Replace("\n",""));
            }
      }

To begin, we obtain the files or folders to move, get the DirID of the destination folder, and finally call the function MoveDirectory(int nDirID, int nParentID) to achieve the movement. Since there is nothing especially unusual going on here and the code is self-explaining, we do not chatter much.

Retrieving attributes

Now we turn our attention to the page ViewDisk.aspx on which we accomplish the task of retrieving file attributes, whose buddy code-behind file is ViewDisk.aspx.cs. As usual, let us first look at its page layout.

Figure 9: Design-time view of page ViewDisk.aspx

Since the controls in Figure 9 are all self-explaining, here we choose to omit detailing them and focus on the page initialization.

Listing 9: Initialization of page ViewDisk.aspx

public partial class ViewDisk : System.Web.UI.Page
{
      int nFileID = -1;
      private int nParentID = -1;
protected void Page_Load(object sender,EventArgs e)
      {
            ///Obtain the value of parameter DirID
            if(Request.Params["DirID"] != null)
            {
                  if(Int32.TryParse(Request.Params["DirID"].ToString(),out nFileID) == false)
                  {
                        return;
                  }
            }
            ///Get the value of parameter ParentID
            if(Request.Params["ParentID"] != null)
            {
                  if(Int32.TryParse(Request.Params["ParentID"].ToString(),out nParentID) == false)
                  {
                        return;
                  }
            }
            if(!Page.IsPostBack)
            {  //Retrieve file attributes from the database using parameter nDirID
                  if(nFileID > -1)
                  {
                        BindFileData(nFileID);
                  }
            }
      }
      private void BindFileData(int nDirID)
      {
            IDisk disk = new Disk();
            SqlDataReader dr = disk.GetSingleFile(nFileID);
            if(dr.Read())
            {   ///Get the file name(including the extension)
                  Name.Text = dr["Name"].ToString();
                  Type.Text = dr["Type"].ToString();
                  Contain.Text = dr["Contain"].ToString() + "B";
                  CreateDate.Text = dr["CreateDate"].ToString();
                  
                  ///Dynamically create the folder under which the file lies
                  Dir.Text = CreateDir(Int32.Parse(dr["DirID"].ToString()));
            }
            dr.Close();
            
      }
public string CreateDir(int nDirID)
      {
            StringBuilder dirSB=new StringBuilder();// create a new string - dirSB
            //Obtain folders from inside the database
            IDisk disk = new Disk();
            DataTable dataTable = SystemTools.ConvertDataReaderToDataTable(disk.GetAllDirectoryFile());   
 
            DataRow[] rowList = dataTable.Select("DirID='" + nDirID.ToString() + "'");
            if(rowList.Length != 1) return("");
            ///Create other folders
            InsertParentDir(dataTable,Int32.Parse(rowList[0]["ParentID"].ToString()),dirSB);
            return (dirSB.ToString());
      }
 
private void InsertParentDir(DataTable dataTable,int nParentID,StringBuilder sDir)
      {
            if(nParentID <= -1)
            {
                  return;
            }
            DataRow[] rowList = dataTable.Select("DirID='" + nParentID.ToString() + "'");
            if(rowList.Length != 1) return;
            //Insert the folder info
            sDir.Insert(0,rowList[0]["Name"].ToString() + "/");
      //Call this function recursively and insert corresponding folder info 
      InsertParentDir(dataTable,Int32.Parse(rowList[0]["ParentID"].ToString()),sDir);
      }
protected void ReturnBtn_Click(object sender,EventArgs e)
      {
            Response.Redirect("~/Default.aspx?ParentID=" + nParentID.ToString());
      }
}

When initialized, page ViewDisk.aspx first obtains parameters nDirID and nParentID from the address bar and then show the retrieved file attributes onto the page, which is achieved by calling function BindFileData(int nDirID). With the help of parameter nDirID, function BindFileData retrieves file attributes from the database and then we use controls Name, Dir, Type, Contains and CreateDate to display the name, hosting folder, type, size, and creating date of the file respectively. There is one thing to notice since we have not directly persisted the folder under which the file lies into the database, we have to dynamically create it. This, obviously seen from above, is finished in functions CreateDir(int nDirID) and InsertParentDir(DataTable dataTable, int nParentID, StringBuilder sDir). The following steps are taken so as to create the folder the file belongs to.

1.    Define a string variable to hold the folder.

2.    Retrieve all the folders from the database and save the results into a DataTable instance.

3.    Get the DirID of the parent directory of the current file and add its name to the string dirSB.

4.    Get the parent directory of the file in Step 3 and iterate over Step 3 until the variable ParentID becomes 1. Here, Steps 1 and 2 are done in function CreateDir(int nDirID), while Steps 3 and 4 are finished within function InsertparentDir.

When we click the button Return, we are navigated back to page Default.aspx (the main page), which is accomplished by only one-line code above.

The last note should be mentioned that, as with the typical ASP.NET applications, in this sample we have also constructed an error handling pageErrorPage.aspx. Whenever there is something wrong, we take the policy to redirect the current page to page ErrorPage.aspx, letting it deal with the error-related information.

Downloads
Conclusion

Whew, what a long ride! That is quite a bit of code to go through, but I hope you will agree that it is not anything incredibly complex to construct such a web disk applicationa popular online storage systemvia ASP.NET 2.0 techniques. Now, if you fell comfortable with it, I highly recommend you to put it into practice.


Product Spotlight
Product Spotlight 

©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-24 1:55:47 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search