Greg Does IT

Do it to get it

About

The blog of Gergely Orosz on Silverlight, .NET and other IT related topics. 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_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.

Shout it kick it on DotNetKicks.com
Bookmark and Share

46 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

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

    hyspdrt

  9. 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

    Bomanden

  10. 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

    Anupam Jana

  11. nice work good solution thanks

    maxishare

  12. 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.

    Eric Fickes

  13. 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}”;

    vidya shekar

  14. 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!

    Toni

  15. 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

    Daniel

  16. 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.

    Michiel

  17. Capture d’écran en Silverlight | Aerilys Blog

  18. The zip url gives a ’404 file not found’

    Ben

  19. 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.

    Jia

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

    Justin

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

    Gergely Orosz

  22. Fantasctic and simple

    Venkateswarlu Eraga

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

    jesus

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

    Sh4dyPT

  25. 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?

    bullmoose20

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

    Aleksandar

  27. where can I find it?…

    [...]Greg Does IT » Blog Archive » File Upload in Silverlight – a Simple Solution[...]…

    I'm glad with that

  28. Server…

    [...]Greg Does IT » Blog Archive » File Upload in Silverlight – a Simple Solution[...]…

    Blog Server

  29. [...] see a few SL file upload examples that seem straightforward enough, but do I have to do something different for Azure than a normal [...]

    How to do file upload in Azure-hosted Silverlight? | SeekPHP.com

  30. Khoc.Vn…

    [...]Greg Does IT » Blog Archive » File Upload in Silverlight – a Simple Solution[...]…

    Khoc.Vn

  31. 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

    gintu

  32. Welcome to GFXshare.us – Free GFX source for you…

    [...]Greg Does IT » Blog Archive » File Upload in Silverlight – a Simple Solution[...]…

    Welcome to GFXshare.us - Free GFX source for you

  33. Hi,

    Any idea why this upload method corrupts .xslx files?

    Martin

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

    Martin

  35. Free Download Software, Games, Movie, eBook, Music, TV-Shows, GFX…

    [...]Greg Does IT » Blog Archive » File Upload in Silverlight – a Simple Solution[...]…

    Free Download Software, Games, Movie, eBook, Music, TV-Shows, GFX

  36. @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.

    John

  37. 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?

    Joe

  38. 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=" + _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.

    Joe

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

    isambert

  40. 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

    hjp

  41. 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.

    hjp

  42. 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.

    Kyle

  43. 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.

    Kyle

  44. 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
    };

    Kyle

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

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

    deepak

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

    Jeremiah

Leave a Reply