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
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.
- Silverlight file upload control on Codeplex – an advanced file upload control
- Silverlight file upload control with file explorer
- A stripped down file upload solution – source is simpler than the ones above, though still quite complex
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.
» File Upload in Silverlight – a Very Simple Solution…
Thank you for submitting this cool story – Trackback from DotNetShoutout…
DotNetShoutout
October 25th, 2009
Cool – I’ve been looking for this kind of simple uploader, thanks!
John Scott
November 2nd, 2009
[...] 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
November 18th, 2009
Hi,
Great solution, but I have an problem with.
When I upload an picture, this uncomplete.
Does you have an idea ?
Thanks
cedric off
December 1st, 2009
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
January 26th, 2010
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
January 27th, 2010
Hello Greg,
do you have same sample but in visual basic???
Thanks a lot.
fausto
February 2nd, 2010
There is a new Silverlight File Upload and Download control suite on codeplex. http://interlink.codeplex.com
hyspdrt
February 9th, 2010
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
February 20th, 2010
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
February 24th, 2010
nice work good solution thanks
maxishare
February 28th, 2010
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
March 2nd, 2010
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
May 5th, 2010
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
July 16th, 2010
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
July 20th, 2010
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
August 4th, 2010
[...] http://gregdoesit.com/2009/10/file-upload-in-silverlight-a-simple-solution/ [...]
Capture d’écran en Silverlight | Aerilys Blog
September 24th, 2010
The zip url gives a ’404 file not found’
Ben
October 22nd, 2010
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
January 23rd, 2011
@Jia: Use WriteStreamClosed to call your recursive upload instead of OpenWriteCompleted. OpenWriteCompleted should still contain your stream writes though.
Justin
February 28th, 2011
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
February 28th, 2011
Fantasctic and simple
Venkateswarlu Eraga
March 21st, 2011
Hi, the solution provided still does not work uploading pictures!
jesus
March 22nd, 2011
Hi, nice post.
You don’t need to call fs.close() inside “using” block.
Sh4dyPT
June 4th, 2011
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
June 13th, 2011
I have the same problem as bullmoose20. I would be very grateful if someone could help us.
Aleksandar
September 1st, 2011
where can I find it?…
[...]Greg Does IT » Blog Archive » File Upload in Silverlight – a Simple Solution[...]…
I'm glad with that
September 29th, 2011
Server…
[...]Greg Does IT » Blog Archive » File Upload in Silverlight – a Simple Solution[...]…
Blog Server
October 3rd, 2011
[...] 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
November 1st, 2011
Khoc.Vn…
[...]Greg Does IT » Blog Archive » File Upload in Silverlight – a Simple Solution[...]…
Khoc.Vn
November 12th, 2011
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
November 22nd, 2011
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
December 6th, 2011
Hi,
Any idea why this upload method corrupts .xslx files?
Martin
December 16th, 2011
Follow up, using hexedit I noticed that the chunks are being saved out of order.
Martin
December 16th, 2011
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
December 20th, 2011
@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
December 27th, 2011
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
January 3rd, 2012
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
January 3rd, 2012
you can use also the Session to save partial byte[] and then create the file only in the last byte upload
isambert
January 6th, 2012
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
January 15th, 2012
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
January 15th, 2012
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
March 19th, 2012
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
March 20th, 2012
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
April 23rd, 2012
In Silverlight, for uploading file this link is very good
http://interview-development.blogspot.in/2012/05/silverlight-upload-file.html
deepak
May 2nd, 2012
How do you do this in PHP? Please respond as your server side script doesn’t work. Thank you
Jeremiah
May 7th, 2012