Greg Does IT

Do it to get it

About

The blog of Gergely Orosz on IT related topics: web, software design, .NET, PHP and the rest. Follow me @gergelyorosz on Twitter.

Recently I wanted to implement a very simple file upload component in Silverlight that uploads a single file and if possible shows upload progress. Having done some search I’ve found numerous examples but all of them seemed too complex for the simple task of uploading a file so I implemented a really lightweight solution. The source code can be downloaded from here: SimpleFileUpload.zip

Uploading files in Silverlight

Uploading files is quite an easy one in Silverlight: it’s basically just a request made to another server and the file contents are passed in this request. A possible way of implementing this is by using the WebClient class:

private void UploadFile()
{
FileStream _data; // The file stream to be read
string uploadUri;
 
byte[] fileContent = new byte[_data.Length]; // Read the contents of the stream into a byte array
_data.Read(fileContent, 0, _data.Length);
 
WebClient wc = new WebClient();
wc.OpenWriteCompleted += new OpenWriteCompletedEventHandler(wc_OpenWriteCompleted);
Uri u = new Uri(uploadUri);
wc.OpenWriteAsync(u, null, fileContent); // Upload the file to the server
}
 
void wc_OpenWriteCompleted(object sender, OpenWriteCompletedEventArgs e) // The upload completed
{
if (e.Error == null)
{
  // Upload completed without error
}

Upload Progress Indicator

The above solution does the job of uploading the file well. However it does not indicate file upload progress at all: when uploading large files or when having slow internet connection this behaviour would be desirable.

Silverlight has no built-in way to monitor the number of bytes sent which means that the only way to indicate upload progress is sending the file to the server in multiple, smaller chunks. Of course this behaviour needs support from the server side as well.

The idea is that multiple calls are made to the server, every call submitting the next chunk of the file. On the server these chunks are appended to the file.

Silverlight code snipplet

public const int CHUNK_SIZE = 4096;
public const string UPLOAD_URI = "http://localhost:55087/FileUpload.ashx?filename={0}&append={1}";
private Stream _data;
private string _fileName;
private long _bytesTotal;
private long _bytesUploaded;
 
private void UploadFileChunk()
{
    string uploadUri = ""; // Format the upload URI according to wether the it's the first chunk of the file
    if (_bytesUploaded == 0)
    {
        uploadUri = String.Format(UPLOAD_URI,_fileName,0); // Dont't append
    }
    else if (_bytesUploaded < _bytesTotal)
    {
        uploadUri = String.Format(UPLOAD_URI, _fileName, 1); // append
    }
    else
    {
        return;  // Upload finished
    }
 
    byte[] fileContent = new byte[CHUNK_SIZE];
    _data.Read(fileContent, 0, CHUNK_SIZE);
 
    WebClient wc = new WebClient();
    wc.OpenWriteCompleted += new OpenWriteCompletedEventHandler(wc_OpenWriteCompleted);
    Uri u = new Uri(uploadUri);
    wc.OpenWriteAsync(u, null, fileContent);
    _bytesUploaded += fileContent.Length;
}
 
void wc_OpenWriteCompleted(object sender, OpenWriteCompletedEventArgs e)
{
    if (e.Error == null)
    {   
        if (_bytesUploaded < _bytesTotal)
        {
            UploadFileChunk();
        }
        else
        {
            // Upload complete
        }
    }
}

Since Silverlight is a client side technology the server side can be implemented in any language. In this example I’ve created .NET and PHP support for the server side.

.NET server side code snipplet

public void ProcessRequest(HttpContext context)
{
    if (context.Request.InputStream.Length == 0)
        throw new ArgumentException("No file input");
    if (context.Request.QueryString["fileName"] == null)
        throw new Exception("Parameter fileName not set!");
 
    string fileName = context.Request.QueryString["fileName"];
    string filePath = @HostingEnvironment.ApplicationPhysicalPath + "/" + fileName;
    bool appendToFile = context.Request.QueryString["append"] != null && context.Request.QueryString["append"] == "1";
 
    FileMode fileMode;
    if (!appendToFile)
    {
        if (File.Exists(filePath))
            File.Delete(filePath);
        fileMode = FileMode.Create;
    }
    else
    {
        fileMode = FileMode.Append;
    }
    using (FileStream fs = File.Open(filePath, fileMode))
    {
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = context.Request.InputStream.Read(buffer, 0, buffer.Length)) != 0)
        {
            fs.Write(buffer, 0, bytesRead);
        }
        fs.Close();
    }
}

PHP server side code snipplet

<?php
//  This is the most basic of scripts with no try catches
$filename = isset($_REQUEST["filename"]) ? $_REQUEST["filename"] : "jjj";
$append = isset($_REQUEST["append"]);
try
{
	if(!$append)
		$file = fopen($filename,"w");
	else
		$file = fopen($filename,"a");
	$input = file_get_contents ("php://input");
	fwrite($file,$input);
	fclose($file);
}
catch (Exception $e) 
{
	echo 'Caught exception: ',  $e->getMessage(), "\n";
}
?>

Give me the code

In my solution I’ve added some small enhancements: a progress bar and a textbox to display file upload status. The Silverlight solution along with the .NET and PHP server side handlers can be downloaded from here: SimpleFileUpload.zip.

Notes for the code

  • Before running the project, set the UPLOAD_URI variable to point to the appropriate .asmx or .php file
  • The script is not suited for production environment because of the following:
    • Files are uploaded directly to the root directory of the web application
    • The files are created and constantly appended to. A more desirable approach would be to store the unfinished files in a temp folder until upload is complete and then move them to the upload folder


Other File Upload Resources

Finally, for those looking for a more advanced solution, here is a list of more advanced (and more complex) file upload components with links to their download pages.

Shout it kick it on DotNetKicks.com
Bookmark and Share

7 Responses to “File Upload in Silverlight - a Simple Solution”

  1. ยป File Upload in Silverlight - a Very Simple Solution…

    Thank you for submitting this cool story - Trackback from DotNetShoutout…

    DotNetShoutout

  2. Cool - I’ve been looking for this kind of simple uploader, thanks!

    John Scott

  3. [...] Gergely Orosz target to implement a simple solution in uploading file using Silverlight. The upload control contains a progress indicator where you can keep track of the upload progress. The sample also showed you how you can collect the uploaded file using PHP or C#. [...]

    Simple File Upload Solution | Silverlike - A Free Microsoft Silverlight 3 Directory

  4. Hi,

    Great solution, but I have an problem with.
    When I upload an picture, this uncomplete.
    Does you have an idea ?
    Thanks

    cedric off

  5. thanks for sharing. i was also just looking for a simple solution to use on our electronic dance music site writter in silverlight.

    jason callison

  6. i got a question, how do i save the uploaded file to my server specific location?
    In asp.net, it is using SaveAs(Server.Path(”../location”));
    so how in silverlight?

    DEN

  7. Hello Greg,

    do you have same sample but in visual basic???

    Thanks a lot.

    fausto

Leave a Reply