Click here to Skip to main content
11,639,554 members (62,823 online)
Click here to Skip to main content

Silverlight File Manager

, 8 Jan 2012 CPL 12.4K 1.2K 14
Rate this:
Please Sign up or sign in to vote.
The Silverlight File Manager on the ListBox control based universal server handler may be working on the ASP .NET WebForms and MVC projects. All requests are sent asynchronously via helper class.
Silverlight File Manager

Introduction

Silverlight applications have no direct access to the file system of server. To gain access to the server file system needs to be done the proxy (gateway) page.

I created a gateway class for processing requests. The class can be used in the ASP .NET WebForm and ASP.NET MVC projects.

File Manager is created as custom control based on the ListBox. The control has a method for sending requests to the server.

All requests are sent asynchronously via helper class. The helper class is optimized for File Manager, but it can be easily modified for other purposes.

Data exchange is carried out in the JSON. Is it optimal for traffic volume.

Server

Gateway class for processing requests created on the Class Library project.

The class has a main method - GetResult. The method for processing requests returns a JSON string.

Requests data are taken from HttpContext.Current.Request class. For it, I created a helper variable - Request, and also Server.

HttpRequest Request = HttpContext.Current.Request;
HttpServerUtility Server = HttpContext.Current.Server;

Server can only handle POST requests. Request parameters are available in the Form collection.

Server will handle the six operations:

  • check - check file name
  • upload - upload and save file on the server
  • newdir - create a new directory
  • delete - delete file or directory
  • rename - change name of the file or directory
  • get (default) - get file and directories list

Name of the operation will be contained in the parameter cmd.

string cmd = "";
if (!String.IsNullOrEmpty(Request.Form["cmd"])) { cmd = Request.Form["cmd"].ToLower(); }

The root directory name contains a _Root variable. The client must pass a relative path on the path field.

string _Root = ""; // empty - server root directory
string path = "";
if (!String.IsNullOrEmpty(Request.Form["path"])) 
	{ path = Request.Form["path"]; } else { path = "/"; }
if (!path.EndsWith("/")) path += "/";

Before executing the operation (cmd), the server must verify the existence of a directory.

DirectoryInfo DI = new DirectoryInfo
			(Server.MapPath(String.Format("~/{0}{1}", _Root, path)));
if (!DI.Exists)
{
  result = GetError(String.Format("Error. The directory \"{0}\" not found.", 
		String.Format("~/{0}{1}", _Root, path)));
  return result.ToString();
}

I did not create verification of the user authorization and access, but you can create it.

Server returns JSON string. I created two helper functions for it. First - GetError return JSON object with error message. Second - GetJsonString converting object to JSON.

private StringBuilder GetError(string msg)
{
  return GetJsonString(new { stat = "err", msg = msg });
}

private StringBuilder GetJsonString(object source)
{
  StringBuilder result = new StringBuilder();
  JavaScriptSerializer myJSON = new JavaScriptSerializer();
  myJSON.Serialize(source, result);
  return result;
}

I used anonymous types with next properties:

  • stat - server response code: ok - ok, err - error
  • msg - error message, if stat = err
  • allowUp - has top-level directory (only for file list requests)
  • data - array of files and directories (only for file list requests):
    • name - file or directory name
    • size - file size (only for files)
    • type - item type: 0 - directory, 1 - file
    • url - file URL
public class Gateway
{
  private string _Root = "Custom"; // root directory

  public Gateway() { }

  public string GetResult()
  {
    if (HttpContext.Current == null) throw new Exception("HTTP request is required.");

    HttpRequest Request = HttpContext.Current.Request;
    HttpServerUtility Server = HttpContext.Current.Server;

    StringBuilder result = new StringBuilder();

    try
    {
      // ...
      // here you can make authorization
      // ..

      string cmd = "", path = "";
      if (!String.IsNullOrEmpty(Request.Form["cmd"])) 
		{ cmd = Request.Form["cmd"].ToLower(); }
      if (!String.IsNullOrEmpty(Request.Form["path"])) 
		{ path = Request.Form["path"]; } else { path = "/"; }
      if (!path.EndsWith("/")) path += "/";
        
      DirectoryInfo DI = new DirectoryInfo
		(Server.MapPath(String.Format("~/{0}{1}", _Root, path)));
      if (!DI.Exists)
      {
        result = GetError(String.Format("Error. 
        The directory \"{0}\" not found.", String.Format("~/{0}{1}", _Root, path)));
        return result.ToString();
      }

      if (cmd == "check")
      {
        #region check file name
        if (File.Exists(Path.Combine(DI.FullName, Request.Form["name"])))
        {
          result = GetJsonString(new { stat = "err", msg = String.Format
          ("Sorry, file \"{0}\" is exists on the directory 
		\"{1}\".", Request.Form["name"], path) });
        }
        else
        {
          result = GetJsonString(new { stat = "ok" });
        }
        #endregion
      }
      else if (cmd == "upload")
      {
        #region save file
        if (Request.Files["file1"] == null || Request.Files["file1"].ContentLength <= 0)
        {
          result = GetError("Error. File is required.");
        }
        else
        {
          // check file name
          if (File.Exists(Path.Combine(DI.FullName, Request.Files["file1"].FileName)))
          { 
            result = GetJsonString(new { stat = "err", msg = String.Format
            ("Sorry, file \"{0}\" is exists on the directory \"{1}\".", 
            Request.Files["file1"].FileName, path) });
          }
          else
          { 
            // save
            using (FileStream fs = System.IO.File.Create
            (Path.Combine(DI.FullName, Request.Files["file1"].FileName)))
            {
              byte[] buffer = new byte[4096];
              int bytesRead;
              while ((bytesRead = Request.Files["file1"].
		InputStream.Read(buffer, 0, buffer.Length)) != 0)
              {
                fs.Write(buffer, 0, bytesRead);
              }
            }
            result = GetJsonString(new { stat = "ok" });
          }
        }
        #endregion
      }
      else if (cmd == "newdir")
      {
        #region create a new directory
        if (String.IsNullOrEmpty(Request.Form["name"]))
        {
          result = GetError("Error. Directory name is required.");
        }
        else
        {
          // check name
          DirectoryInfo d = new DirectoryInfo(Path.Combine
		(DI.FullName, Request.Form["name"]));
          if (d.Exists)
          {
            result = GetError("Sorry, directory is exists.");
          }
          else
          {
            // create directory
            d.Create();
            // is ok
            result = GetJsonString(new { stat = "ok" });
          }
        }
        #endregion
      }
      else if (cmd == "delete")
      {
        #region delete file/directory
        if (String.IsNullOrEmpty(Request.Form["name"]))
        {
          result = GetError("Error. Name is required.");
        }
        else
        {
          if (File.GetAttributes(Path.Combine(DI.FullName, 
		Request.Form["name"])) == FileAttributes.Directory)
          {
            // is directory, 
            Directory.Delete(Path.Combine(DI.FullName, Request.Form["name"]), true);
          }
          else
          {
            // is file
            File.Delete(Path.Combine(DI.FullName, Request.Form["name"]));
          }
          result = GetJsonString(new { stat = "ok" });
        }
        #endregion
      }
      else if (cmd == "rename")
      {
        #region rename file/directory
        string oldName = Request.Form["oldName"], newName = Request.Form["newName"];
        if (String.IsNullOrEmpty(oldName) || String.IsNullOrEmpty(newName))
        {
          result = GetError("Error. Name is required.");
        }
        else
        {
          if (newName != oldName)
          {
            if (File.GetAttributes(Path.Combine(DI.FullName, 
			oldName)) == FileAttributes.Directory)
            {
              // rename directory
              Directory.Move(Path.Combine(DI.FullName, oldName), 
			Path.Combine(DI.FullName, newName));
            }
            else
            {
              // rename file
              File.Move(Path.Combine(DI.FullName, oldName), 
			Path.Combine(DI.FullName, newName));
            }
          }
          result  = GetJsonString(new { stat = "ok" });
        }
        #endregion
      }
      else
      {
        #region file list
        ArrayList files = new ArrayList();
        // dicrectories
        foreach (DirectoryInfo d in DI.GetDirectories())
        {
          files.Add(new
          {
            name = d.Name,
            size = 0,
            type = 0, // type = 0 - is directory
            url = String.Format("http://{0}/{1}{2}{3}", Request.Url.Host + 
            (Request.Url.Port > 80 ? ":" + Request.Url.Port.ToString() : ""), 
            _Root, path, d.Name) 
          }); 
        }
        // files
        foreach (FileInfo f in DI.GetFiles())
        {
          files.Add(new
          {
            name = f.Name,
            size = f.Length,
            type = 1,// type = 1 - is file
            url = String.Format("http://{0}/{1}{2}{3}", Request.Url.Host + 
            (Request.Url.Port > 80 ? ":" + Request.Url.Port.ToString() : ""), 
            _Root, path, f.Name)
          }); 
        }
        // check top-level directory
        bool allowUp = !String.IsNullOrEmpty(path.Trim("/".ToCharArray()));
        // create JSON
        result = GetJsonString(new { stat = "ok", allowUp = allowUp, data = files });
        #endregion
      }
    }
    catch (Exception ex)
    {
      // error
      result = GetError(ex.Message);
    }

    // result
    return result.ToString();
  }

  /// <span class="code-SummaryComment"><summary>
</span>

The Gateway class is easy to use ASP. NET WebForms. For example, in the ASP .NET Handler (Gateway.ashx).

public class Gateway : IHttpHandler
{
  public void ProcessRequest(HttpContext context)
  {
    Nemiro.FileManager.Common.Gateway myGateway = 
		new Nemiro.FileManager.Common.Gateway();
    context.Response.ContentType = "application/json";
    context.Response.Write(myGateway.GetResult());
  }

  public bool IsReusable
  {
    get
    {
      return false;
    }
  }
}

And also in the ASP .NET MVC. For example, to the Gateway Action in HomeController.

public class HomeController : Controller
{
  [HttpPost]
  public ActionResult Gateway()
  {
    Nemiro.FileManager.Common.Gateway myGateway = 
		new Nemiro.FileManager.Common.Gateway();
    return new ContentResult() { Content = myGateway.GetResult(), 
	ContentType = "application/json", ContentEncoding = System.Text.Encoding.UTF8 };
  }
}

Silveright (client)

WebHelper Class

The WebHelper class implements the ability to send asynchronous HTTP requests.

For request parameters, I created two additional classes.

First - the QueryItem class for parameter data. The QueryItem class can contain text data and files. Second - the QueryItemCollection collections of QueryItem.

The WebHelper class has a one public method - Execute. The method takes a reference to a callback function.

For callback function, I created delegate.

public delegate void WebCallback(string stat, string msg, bool allowUp, 
	JsonValue data, object tag);

As you can see, to the callback function will be transferred server response from JSON. It is special for File Manager, but you can change delegate and callback function, it is easy.

public class WebHelper
{
  /// <span class="code-SummaryComment"><summary>
</span>

FileList Control

The FileList control inherited from ListBox. Each item will also be custom.

FileItem

The FileItem class inherited from StackPanel. The Item will contain an icon, name, file size and 3 buttons for open, rename and delete item. But the class cannot independently send requests to server. This is only possible via FileList (Parent).

public class FileItem : StackPanel
{
  /// <span class="code-SummaryComment"><summary>
</span>

FileList

The FileList contains a helper method for showing errors via standard MessageBox. Because the requests are executed in separate threads, MessageBox can be called only from the main thread via BeginInvoke.

private void ShowError(string msg)
{
  this.Dispatcher.BeginInvoke(() =>
  {
    MessageBox.Show(msg, "Error", MessageBoxButton.OK);
  });
}

To implement the ProgressBar, the class contain two events: Process and Complete.

public event EventHandler Process;
public event EventHandler Complete;

ProgressBar will not be to the FileList, is it external (to page). For progress, I created child windows.

private string _Url = "http://localhost:58646/Gateway.ashx"; // gateway url for requests
private PleaseWait myPleaseWait = null;

public MainPage()
{
  InitializeComponent();

  fileList1.Url = _Url; //set url

  // handlers for progress
  fileList1.Process += new EventHandler(fileList1_Process);
  fileList1.Complete += new EventHandler(fileList1_Complete);
}

private void fileList1_Process(object sender, EventArgs e)
{
  this.Dispatcher.BeginInvoke(() =>
  {
    if (myPleaseWait != null && 
    myPleaseWait.Visibility == System.Windows.Visibility.Visible) return;
    // show porgress
    myPleaseWait = new PleaseWait();
    myPleaseWait.Show();
  });
}

private void fileList1_Complete(object sender, EventArgs e)
{
  this.Dispatcher.BeginInvoke(() =>
  {
    //close progress
    if (myPleaseWait != null)
    {
      myPleaseWait.Close();
      myPleaseWait = null;
    }
    // set new path from fileList
    tbPath.Text = fileList1.Path;
  });
}

For upload files, I created helper class UploadItem because sending is done in two stages:

  1. check
  2. upload
public class UploadItem
{
  /// <span class="code-SummaryComment"><summary>
</span>

The FileList control has a UploadList collection.

private List<UploadItem> _UploadFiles = null;

And method for adding UploadItem to collection.

public void AddUploadItem(FileInfo f)
{
  if (_UploadFiles == null) _UploadFiles = new List<uploaditem />();
  UploadItem itm = new UploadItem(_UploadFiles.Count, f, this.Url, this.Path);
  itm.Complete += new EventHandler(UploadItem_Complete);
  _UploadFiles.Add(itm);
}

The FileList can take files via Drag and Drop.

this.Drop += new DragEventHandler(FileList_Drop);
private void FileList_Drop(object sender, DragEventArgs e)
{
  // add selected files to upload list
  FileInfo[] files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];
  foreach (FileInfo f in files)
  {
    this.AddUploadItem(f); 
  }

  // upload files
  this.Upload();
}

Uploading files starting on the Upload method each from _UploadList.

public void Upload()
{
  if (_UploadFiles == null || _UploadFiles.Count <= 0) return; // upload list is empty
      
  _UploadErrorMessages = new StringBuilder();

  if (Process!=null) Process(this, null);;

  foreach (UploadItem itm in _UploadFiles)
  {
    itm.Run();//start upload file
  }
}

After sending the file to the server, it is removed from _UploadList on the UploadItem_Complete handler. When the files over, FileList update the list of files from server.

private void UploadItem_Complete(object sender, EventArgs e)
{
  this.Dispatcher.BeginInvoke(() =>
  {
    UploadItem itm = sender as UploadItem;
    if (itm.State == UploadItem.StateList.Error)
    {
      _UploadErrorMessages.AppendLine(itm.Message);
    }
    // remove file from upload list
    _UploadFiles.Remove(itm);
    if (_UploadFiles.Count == 0)
    {
      // upload list is empty
      // start Complete
      if (Complete != null) Complete(this, null);
      // has error?
      if (_UploadErrorMessages.Length <= 0)
      {
        // no error, update file list
        UpdateFileList();
      }
      else
      {
        // show error message
        ShowError(_UploadErrorMessages.ToString());
      }
    }
  });
}

The entire code of the FileList class.

public class FileList : ListBox
{

  /// <span class="code-SummaryComment"><summary>
</span>

Using the code

Main files of FileList control: FileList.cs, FileItem.cs, WebHelper.cs and UploadItem.cs (FileManage project). For server - Gateway.cs (FileManager.Common project).

Add FileList to Silverlight page.

<my:FileList Height="256" HorizontalAlignment="Left" 
	Margin="12,41,0,0" x:Name="fileList1" VerticalAlignment="Top" 
	Width="573" Grid.ColumnSpan="2" Grid.RowSpan="2" />

Add the Gateway code to ASP .NET handler (ashx for WebForms) or action (for MVC) and run the server.

FileManager.Common.Gateway myGateway = new FileManager.Common.Gateway();
context.Response.ContentType = "application/json";
context.Response.Write(myGateway.GetResult());

Set Url property for FileList to the Gateway server page.

public MainPage()
{
  InitializeComponent();
  fileList1.Url = "http://localhost:58646/Gateway.ashx"; // you gateway url
  // http://localhost:58646 - is default address for solution but maybe another
}

Enjoy!

License

This article, along with any associated source code and files, is licensed under The Common Public License Version 1.0 (CPL)

Share

About the Author

Alеksey Nemiro
Web Developer Kbyte.Ru
Russian Federation Russian Federation
Web Developer. Writer. Author of the Kbyte.Ru.
I like ASP .NET WebForms/MVC, C#, Visual Basic .NET, T-SQL, JavaScript, HTML, CSS, PHP.

You may also be interested in...

Comments and Discussions

 
QuestionMy vote of 5 Pin
liviucatrina8-May-12 21:01
memberliviucatrina8-May-12 21:01 
GeneralMy vote of 5 Pin
pgmckillop9-Jan-12 18:45
memberpgmckillop9-Jan-12 18:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150731.1 | Last Updated 8 Jan 2012
Article Copyright 2012 by Alеksey Nemiro
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid