In order to upload a file from a Silverlight application to your webserver you generally provide a WCF service that accepts the filestream. However, accepting a large file as a single chunk can sometimes lead to problems with service timeouts, request size restrictions etc. so I took the time to write a chunked file uploader service, which allows you to send the file in several parts. The way this works is similar to how a normal file stream works, in that you open a target file in exchange for a token/handle, write to the file using the token/handle and then close the file when you are finished. In addition to this, because we are dealing with async operations over HTTP we are required to ensure the file parts are committed in the correct order as they were intended when sent (i.e. sequencing the chunks). Firstly, the interface for the IChunkedFileUploader is as follows:

[ServiceContract]
    public interface IChunkedFileUpload
    {
        [OperationContract]
        string OpenChunkedUploadFile();

        [OperationContract]
        Response WriteToChunkedUploadFile(string fileToken, int seq, byte[] data);

        [OperationContract]
        Response CloseChunkedUploadFile(string fileToken, int totalPartsSent);
    }
The 'Response' class simply wraps two properties { bool, string } for the success flag and any error message. The implementation of this interface in my WCF layer looks as follows:

public class FileUploadService : IChunkedFileUpload
    {
        /* In order to allow the chunking of upload data in Silverlight we will write a light sequenced data wrapper */
        private class FilePart
        {
            public int Seq { get; set; }
            public byte[] Data { get; set; }
        }
        private static Dictionary<string, List<FilePart>> _FileParts = new Dictionary<string, List<FilePart>>();

        public string OpenChunkedUploadFile()
        {
            string fileToken = "";


            //incase of simultaneous calls, lock the static variable
            lock (_FileParts)
            {
                //find a unique name for the file
                while (fileToken == "" || _FileParts.ContainsKey(fileToken))
                {
                    fileToken = DateTime.Now.Ticks.ToString() + ".ccf";
                }

                //create the "file"
                _FileParts.Add(fileToken, new List<FilePart>());
            }

            //return the token (filename)
            return fileToken;
        }

        public Response CloseChunkedUploadFile(string fileToken, int totalPartsSent)
        {

            try
            {

            //check its a valid token, else its 'file not found'
            if(_FileParts.ContainsKey(fileToken))
            {
                //check the number of parts received matches the 'total parts' check passed in by client (to ensure all parts were received, including the final part)
                if (_FileParts[fileToken].Count == totalPartsSent)
                {
                    List<byte> fileData = new List<byte>();

                    //reconstruct the file data from the file parts
                    //ensuring the sequences are valid (no gaps, duplicates etc.)
                    for (int i = 0; i <= _FileParts[fileToken].Max(p=>p.Seq); i++)
                    {
                        IEnumerable<FilePart> seqPart = _FileParts[fileToken].Where(p => p.Seq == i);

                        //should be 1 match
                        if (seqPart.Count() == 1)
                            fileData.AddRange(seqPart.First().Data);
                        else
                            throw new System.IO.InvalidDataException("Data was invalid, not all sequences were present or were duplicated");
                    }

                    //if we reach this point then the total number of parts was correct and the sequences were correctly sent/received
                    //we can now write the bytes to disk
                    string fullFilePath = HttpContext.Current.Server.MapPath("UploadFiles\\" + fileToken);
                    System.IO.File.WriteAllBytes(fullFilePath, fileData.ToArray());

                    //and now kill the version in memory
                    _FileParts[fileToken].Clear();
                    _FileParts.Remove(fileToken);

                    //success!
                    return new Response() { IsSuccess = true };
                }
                else
                {
                    throw new System.IO.InvalidDataException("Data was invalid, total parts check failed.");
                }
            }
            else{
                throw new System.IO.FileNotFoundException();
            }
            }
            catch (Exception ex)
            {
                return new Response<string>() { IsSuccess = false, MessageKey = ex.Message };
            }
        }

        public Response WriteToChunkedUploadFile(string fileToken, int seq, byte[] data)
        {
            try
            {
                //check its a valid token, else its 'file not found'
                if (_FileParts.ContainsKey(fileToken))
                {
                    _FileParts[fileToken].Add(new FilePart() { Data = data, Seq = seq });
                    return new Response() { IsSuccess = true };
                }
                else
                {
                    throw new System.IO.FileNotFoundException();
                }
            }
            catch (Exception ex)
            {
                return new Response() { IsSuccess = false, MessageKey = ex.Message };
            }
        }

    }
Basically, what this does is creates a shared 'File system' on the server which is backed by a Dictionary<string, List<FilePart>> - the token/handle and the file parts received. In Silverlight, or any other client, you call 'OpenChunkedUploadFile' to receive a unique token which you will use to send your file parts. This initialises a new item in the dictionary of files. The token will also be used as the final filename, so that you can calculate the remote URL of the eventual file. You then loop through your target file bytes, sending chunks of your desired size to the 'WriteToChunkedUploadFile' function. You mark each file part with the sequence number to ensure the file is rebuilt in the correct order. At this point all the service is doing is adding to the List for the current file in the Dictionary. When all of your bytes are sent, you call 'CloseChunkedUploadFile' passing your token and the count of total file parts (to ensure all parts were received). After performing some checks on the file parts (e.g. number of parts, sequence contiguity) the file parts are arranged in sequence order to create the full file bytes of the file, which is then committed to the physical disk on the server (using a pre-defined folder and the token file name). I have written an upload helper class which shows this:

public class ChunkedFileUploadHelper
    {
        #region "Fields"
        private int _FileChunkSize;
        private int _fileUploadSequence = 0;
        private int _fileUploadPartSuccessCount = 0;

        private ChunkedUploadFileInfo _fileInfo;
        #endregion

        #region "Events"
        public delegate void UploadAsyncCompletedEventHandler(ChunkedFileUploadHelper sender, string serverFileToken);
        public event UploadAsyncCompletedEventHandler UploadAsyncCompleted;
        protected void OnUploadAsyncCompleted()
        {
            if (UploadAsyncCompleted != null)
                UploadAsyncCompleted(this, _fileInfo.ServerToken);
        }

        #endregion

        /// <summary>
        /// Create an instance of the helper to be used with the provided file data instance
        /// </summary>
        /// <param name="fileInfo">The instance of chunked file data to be used</param>
        public ChunkedFileUploadHelper(ChunkedUploadFileInfo fileInfo) : this(fileInfo, 512000) //default chunk size 512KB
        { }

        /// <summary>
        /// Create an instance of the helper to be used with the provided file data instance, specifying a custom chunk size
        /// </summary>
        /// <param name="fileInfo">The instance of chunked file data to be used</param>
        /// <param name="chunkSize">Custom chunk size, default is 512KB</param>
        public ChunkedFileUploadHelper(ChunkedUploadFileInfo fileInfo, int chunkSize)
        {
            if (fileInfo == null)
                throw new ArgumentNullException("fileInfo", "fileInfo cannot be NULL");

            _fileInfo = fileInfo;
            _FileChunkSize = chunkSize;
        }

        /// <summary>
        /// Asynchronously uploads a file using the chunked service and calls the callback passing in the server file token
        /// </summary>
        public void UploadAsync()
        {
            if (_fileInfo != null && _fileInfo.FileData.Length > 0)
            {
                //reset the file upload data
                _fileUploadSequence = 0;
                _fileUploadPartSuccessCount = 0;
                _fileInfo.BytesUploaded = 0;

                //begin with making the initial service call
                ServiceInerfaceClient svc = ServiceUtility.GetChunkedFileUploadClient();

                //first thing to do is to create a new empty file on the server
                svc.OpenChunkedUploadFileCompleted += new EventHandler<OpenChunkedUploadFileCompletedEventArgs>(svc_OpenChunkedUploadFileCompleted);
                svc.OpenChunkedUploadFileAsync();
            }
            else
            {
                throw new InvalidOperationException("Cannot start upload as there was no file data to upload");
            }
        }

        void svc_OpenChunkedUploadFileCompleted(object sender, OpenChunkedUploadFileCompletedEventArgs e)
        {
            ((ServiceInerfaceClient)sender).OpenChunkedUploadFileCompleted -= svc_OpenChunkedUploadFileCompleted;

            if (e.Error == null && !string.IsNullOrEmpty(e.Result))
            {
                //we have been given a filename/token
                _fileInfo.ServerToken = e.Result;

                //now can move on to part 2
                Upload_part2();
            }
            else
            {
                if (e.Error != null)
                    throw e.Error;
                else
                    throw new Exception("Did not receive valid token from the server");
            }
        }

        private void Upload_part2()
        {
            //create a service client and add the handle for the part uploaded event
            ServiceInerfaceClient svc = ServiceUtility.GetChunkedFileUploadClient();
            svc.WriteToChunkedUploadFileCompleted += new EventHandler<WriteToChunkedUploadFileCompletedEventArgs>(svc_WriteToChunkedUploadFileCompleted);

            //start the recursion
            Upload_part2_recursion(svc);
        }



        private void Upload_part2_recursion(ServiceInerfaceClient svc)
        {
            //now that we have a file name we can send chunks of data
            byte[] bytesToSend = _fileInfo.FileData.Skip(_fileUploadSequence * _FileChunkSize).Take(_FileChunkSize).ToArray();

            if (bytesToSend.Length > 0)
            {
                svc.WriteToChunkedUploadFileAsync(_fileInfo.ServerToken, _fileUploadSequence, bytesToSend, bytesToSend.Length);
                _fileUploadSequence++;
            }
        }

        void svc_WriteToChunkedUploadFileCompleted(object sender, WriteToChunkedUploadFileCompletedEventArgs e)
        {
            if (e.Error == null && e.Result != null && e.Result.IsSuccess)
            {
                //bytes were successfully uploaded
                _fileInfo.BytesUploaded += (int)e.UserState;
                _fileUploadPartSuccessCount++;

                //were all bytes accounted for and successfully received?
                if (_fileInfo.BytesUploaded == _fileInfo.FileData.Length && _fileUploadPartSuccessCount == _fileUploadSequence)
                {
                    //we can now remve the handler
                    ((ServiceInerfaceClient)sender).WriteToChunkedUploadFileCompleted -= svc_WriteToChunkedUploadFileCompleted;

                    //file is completely upload, we can now go to step 3
                    Upload_part3();
                }
                else
                {
                    //keep sending the parts 
                    Upload_part2_recursion(((ServiceInerfaceClient)sender));
                }

            }
            else
            {
                if (e.Error != null)
                    throw e.Error;
                else if (e.Result != null)
                    throw new Exception(e.Result.MessageKey);
                else
                    throw new Exception("Unknown Error Uploading File to Server");
            }
        }

        private void Upload_part3()
        {
            ServiceInerfaceClient svc = ServiceUtility.GetChunkedFileUploadClient();

            //now we want to tell the server to close the file and commit to disk
            svc.CloseChunkedUploadFileCompleted += new EventHandler<CloseChunkedUploadFileCompletedEventArgs>(svc_CloseChunkedUploadFileCompleted);
            svc.CloseChunkedUploadFileAsync(_fileInfo.ServerToken, _fileUploadSequence);
        }

        void svc_CloseChunkedUploadFileCompleted(object sender, CloseChunkedUploadFileCompletedEventArgs e)
        {
            if (e.Error == null && e.Result != null && e.Result.IsSuccess)
            {
                //the file is now on the server
                OnUploadAsyncCompleted();
            }
            else
            {
                if (e.Error != null)
                    throw e.Error;
                else if (e.Result != null)
                    throw new Exception(e.Result.MessageKey);
                else
                    throw new Exception("Unknown Error Committing File to Server");
            }
        }

    }
The 'ChunkedFileUploadInfo' class is as follows:

public class ChunkedUploadFileInfo : INotifyPropertyChanged
    {
        #region "Client Side File Info"
        private string _OriginalFilename;
        public string OriginalFilename
        {
            get { return _OriginalFilename; }
            set { _OriginalFilename = value; OnPropertyChanged("OriginalFilename"); }
        }

        private byte[] _fileData;
        public byte[] FileData
        {
            get { return _fileData; }
            set { _fileData = value; OnPropertyChanged("FileData"); }
        }
        #endregion

        #region "Upload FIle Info"
        private string _ServerToken;
        public string ServerToken
        {
            get { return _ServerToken; }
            set { _ServerToken = value; OnPropertyChanged("ServerToken"); }
        }

        private int _bytesUploaded;
        public int BytesUploaded
        {
            get { return _bytesUploaded; }
            set { _bytesUploaded = value; OnPropertyChanged("BytesUploaded"); }
        }
        #endregion

        #region "Notify Property Changed"
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }