File Upload in Silverlight – a Simple Solution

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_v2.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
int bytesRead = _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, new object[] { fileContent, bytesRead }); // 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)
    {   
        object[] objArr = e.UserState as object[];
        byte[] fileContent = objArr[0] as byte[];
        int bytesRead = Convert.ToInt32(objArr[1]);
        Stream outputStream = e.Result;
        outputStream.Write(fileContent, 0, bytesRead);
        outputStream.Close();
        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_v2.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.

Update: I’ve fixed the issue that had to do with corrupt uploads, thanks to Daniel for pointing that out in the comments. I’ve also fixed the ASP.NET server side issue to do with throwing an IOException.

48 Responses to File Upload in Silverlight – a Simple Solution
  1. Trackback: DotNetShoutout
    » File Upload in Silverlight - a Very Simple Solution... Thank you for submitting this cool... dotnetshoutout.com/-File-Upload-in-Silverlight-a-Very-Simple-Solution
  2. John Scott

    Cool – I’ve been looking for this kind of simple uploader, thanks!

  3. [...] Gergely Orosz target to implement a simple solution in uploading file using Silverlight. The upload cont... silverlike.net/simple-file-upload-solution
  4. cedric off

    Hi,

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

  5. jason callison

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

  6. DEN

    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?

  7. fausto

    Hello Greg,

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

    Thanks a lot.

  8. hyspdrt

    There is a new Silverlight File Upload and Download control suite on codeplex. http://interlink.codeplex.com

  9. Bomanden

    Hi great example .. Any of you php sharks who can provide that bit of code that Greg is talking about : eg: copy unfinished files in a temp folder until upload is complete and then move them to the upload folder

    Cheers /Bo

  10. Anupam Jana

    Hi,
    Great example.
    When I execute the application using
    public const string UPLOAD_URI = “http://localhost:2502/FileUpload.ashx?filename={0}&append={1}”;
    It is working fine.
    But when I deployed this application in IIS server and try to upload file, it is not uploading file.
    Than I change the public const string UPLOAD_URI = “http://10.0.20.220:2502/TestFileUpload/FileUpload.ashx?filename={0}&append={1}”;
    TestFileUpload in the virtual dir.

    Please can you tell me what I am doing worng.
    Thanks in advance.

    Regards,
    Anuapm
    Anupam.jana@enfs.com/anupam_jana@yahoo.com

  11. maxishare

    nice work good solution thanks

  12. Eric Fickes

    Hey Gergely,

    http://www.yourethemannowdog.com!

    Thanks for posting this. I just tried it in my own SL / ASP.NET project and it worked like a champ!

    BTW, have you ever made an SL uploader that returns data back to SL? I was hoping for the server to return the uploaded path on complete.

  13. vidya shekar

    Can any one please tell how to run this application after published in iis

    string UPLOAD_URI = “http://localhost:50323/FileUpload.ashx?filename={0}&append={1}”;

  14. Toni

    Hi Greg,

    I’ve tested out the code and it Throws an IOException (damn!) at me.

    I realise it might be a bit late to post right now (duh), but I’m using Silverlight 4 and have many problems with file upload.

    Is it really that hard?

    The application I’m programming is a simple crop and resize app that only needs Upload implemented now – and it’s driving me crazy.

    Thanks in advance!

  15. Daniel

    hi,

    for all interested there is a little bug in code which causes incomplete file uploads.

    Here is the fix for this:

    instead of line:
    _data.Read(fileContent, 0, CHUNK_SIZE);
    write:
    int bytesRead = _data.Read(fileContent, 0, CHUNK_SIZE);
    instead of line:
    wc.OpenWriteAsync(u, null, fileContent);
    write:
    wc.OpenWriteAsync(u, null, new object[]{ fileContent, bytesRead});
    instead of lines:
    byte[] fileContent == e.UserState as byte[];
    Stream outputStream = e.Result;
    outputStream.Write(fileContent, 0, fileContext.length);
    outputStream.Close();
    write:
    object[] objArr = e.UserState as object[];
    byte[] fileContent = objArr[0] as byte[];
    int bytesRead = Convert.ToInt32(objArr[1]);
    Stream outputStream = e.Result;
    outputStream.Write(fileContent, 0, bytesRead);
    outputStream.Close();
    and u are done.

    hope this help someone else.

    with kind regards
    daniel

  16. Michiel

    The first Silverlight Uploader you mention is based on another Silverlight Uploader.
    http://www.michielpost.nl/Silverlight/MultiFileUploader/

    This one is still being supported and new features are added.
    It’s also open source and available on CodePlex.

  17. [...] http://gregdoesit.com/2009/10/file-upload-in-silverlight-a-simple-solution/ [...]... aerilys.fr/blog/?p=231
  18. Ben

    The zip url gives a ‘404 file not found’

  19. Jia

    When trying to upload a 9mb file, half-way through, it returned a IOException: The process cannot access the file… because another process is using it. My guess is that since this is asynchronous, when all those chunks come in, different processes are trying to access the file at the same time.

  20. Justin

    @Jia: Use WriteStreamClosed to call your recursive upload instead of OpenWriteCompleted. OpenWriteCompleted should still contain your stream writes though.

  21. Gergely Orosz

    I’ve just updated the solution to fix the IOException issue as well as the problems with larger files. Sorry about the delay!

  22. Venkateswarlu Eraga

    Fantasctic and simple

  23. jesus

    Hi, the solution provided still does not work uploading pictures! :(

  24. Sh4dyPT

    Hi, nice post.
    You don’t need to call fs.close() inside “using” block.

  25. bullmoose20

    Having trouble with the PHP server side script. No matter which file I select when I hit upload file and search for files, they never show up on the server side in PHP? Any clues on how I can troubleshoot this?

  26. Aleksandar

    I have the same problem as bullmoose20. I would be very grateful if someone could help us.

  27. where can I find it?... [...]Greg Does IT » Blog Archive » File Upload in Silver... filemanagersoft.com/download.html
  28. Trackback: Blog Server
    Server... [...]Greg Does IT » Blog Archive » File Upload in Silverlight – ... serverhelfer.de/webview-0-6-12/trackback
  29. [...] see a few SL file upload examples that seem straightforward enough, but do I have to do something differ... seekphp.com/how-to-do-file-upload-in-azure-hosted-silverlight
  30. Trackback: Khoc.Vn
    Khoc.Vn... [...]Greg Does IT » Blog Archive » File Upload in Silverlight –... khoc.vn/tos
  31. gintu

    i downloaded the code n run it. it showed me the message upload completed but i didn’t find any upload file in .web folder

  32. Welcome to GFXshare.us - Free GFX source for you... [...]Greg Does IT » Blog Archive &... gfxshare.us/fashion-images-videos
  33. Martin

    Hi,

    Any idea why this upload method corrupts .xslx files?

  34. Martin

    Follow up, using hexedit I noticed that the chunks are being saved out of order.

  35. Free Download Software, Games, Movie, eBook, Music, TV-Shows, GFX... [...]Greg Does IT &raqu... 4downloader.com/main/rss.xml
  36. John

    @Martin — Well, yes, you have identified a flaw, and there is no reason to believe this design would work as it stands…. There has to be added to it a mechanism to ensure the packets are reassembled in correct order…kind of like is done in TCP.

  37. Joe

    So how would you go about putting it in order? This method does work very well and my php code works perfect just out of order, maybe some packet loss but that can be easily fixed zipping the file when sending the bytes then unzipping on the server. Any examples how how to put this in order using a simple mechanism?

  38. Joe

    Nevermind, I have found a solution. I have added the following code. AFTER it writes to the outputstream you must call writestreamclosed event:

    private void wc_WriteStreamClosed(object sender, WriteStreamClosedEventArgs e)
    {
    if (e.Error == null)
    {
    if (_imgbytesUploaded < _imgbytesTotal)
    {
    string CurrentTime = GetTimestamp(DateTime.Now.ToUniversalTime());
    UploadFile(_imgName, CurrentTime, _imgdata);
    }
    else
    {
    currentlyuploading = false;
    string CurrentTime = GetTimestamp(DateTime.Now.ToUniversalTime());
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.gintechsystems.com/uploadwpmobilephotocomplete.php?imgName=&quot; + _imgName + "&imgSize=" + _imgbytesTotal + "&gid=" + gintechuser.UserID + "&t=" + CurrentTime);
    request.Method = "GET";
    request.BeginGetResponse(new AsyncCallback(GetResponseUserCallback), request);
    }
    }
    }

    I also enabled the following (not sure if it helps though lol)

    wc.AllowReadStreamBuffering = true;
    wc.AllowWriteStreamBuffering = true;

    Hope this helps somehow.

  39. isambert

    you can use also the Session to save partial byte[] and then create the file only in the last byte upload

  40. hjp

    Hi, I downloaded your new version and still get corrupted file for xlsx and so I tried 300kb text file and found out the text written in it is not in sequence so if we make 4 round trip to server the 3 write event is writing it data even before 2 write event and I am sure that can mess up any xlsx or other file. Please let me know. Thanks in advance

  41. hjp

    Hi, Sorry for the previous post, I am using vb.net and I had to make the following change to make it work correctly
    _bytesUploaded += (fileContent.Length – 1)
    instead of
    _bytesUploaded += fileContent.Length
    Thanks anyway for the simple coding I am using your cause its pretty simple for my small application.

  42. Kyle

    I’m afraid this approach doesn’t seem to work very well for me, I made a similar solution to this and upon finding this page I notice your solution also corrupts files during upload.

  43. Kyle

    Right I think I know what’s wrong:

    What i found works is make a get request to a php file returning the size of the file after the output stream is closed.

    I proceed to only upload the next chunk only when the request returns a value that equals the current stream position – which I’ve found they don’t at the first call, have to sleep the thread about 10ms.

  44. Kyle

    Actually, sorry Joe I didn’t read over your posts – you were right. This is how you prevent file corruption, you write the next chunk on the write closed event. Simply add this

    wc.WriteStreamClosed += delegate(object sender, WriteStreamClosedEventArgs e)
    {
    // upload next chunk
    };

  45. deepak

    In Silverlight, for uploading file this link is very good :)

    http://interview-development.blogspot.in/2012/05/silverlight-upload-file.html

  46. Jeremiah

    How do you do this in PHP? Please respond as your server side script doesn’t work. Thank you :D

  47. Verica

    About file corruption I agree with Kyle. Just split the code:
    void wc_WriteStreamClosed(object sender, WriteStreamClosedEventArgs e)
    {
    if (e.Error == null)
    {

    if (_bytesUploaded < _bytesTotal)
    {
    UploadFileChunk();
    }
    else
    {
    textBox.Text = "Upload complete!";

    }
    }
    }

    void wc_OpenWriteCompleted(object sender, OpenWriteCompletedEventArgs e)
    {
    progressBar.Value = _bytesUploaded;
    if (e.Error == null)
    {
    object[] objArr = e.UserState as object[];
    byte[] fileContent = objArr[0] as byte[];
    int bytesRead = Convert.ToInt32(objArr[1]);
    Stream outputStream = e.Result;
    outputStream.Write(fileContent, 0, bytesRead);
    outputStream.Close();

    }
    }

  48. Audy

    Thank you for your code, acctually it works like charm, but i have this speed problem, when i upload only 22 Mb in my localhost, why it takes so long?? apparently im new in silverlight, may be you could give me an explanation about this?