Uploading multiple files from Silverlight using an ASP.NET Compatible HTTP POST

I was recently trying to make Silverlight upload multiple files to an ASP.NET page using a valid HTTP form POST. It was important that the HttpContext.Request.Files property worked correctly. Little did I realise just how finicky ASP.NET is with HTTP requests. It is critical that TWO line feeds are added between the content-type header in the boundary and the binary data, and ONE line feed must be included after the data prior to the next boundary.

Here’s the Silverlight code:

public static class HttpHelper
{
    public static void UploadFilesAsync(Uri address, IEnumerable<FileInfo> files, Action<string> onCompleted)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address);
        request.Method = "POST";
        string boundary = "-----------------------------" + Guid.NewGuid().ToString().Substring(0, 8);
        request.ContentType = "multipart/form-data; boundary=" + boundary;
        request.BeginGetRequestStream((getRequestResult) =>
        {
            using (Stream requestStream = request.EndGetRequestStream(getRequestResult))
            {
                IEnumerable<FileInfo> uploadFiles = (IEnumerable<FileInfo>)getRequestResult.AsyncState;
                foreach (var uploadFile in uploadFiles)
                {
                    using (Stream fileStream = uploadFile.OpenRead())
                    {
                        WriteText("--" + boundary + Environment.NewLine, requestStream);
                        WriteText(@"Content-Disposition: form-data; name=""fileUpload""; filename=""" + uploadFile.Name + @"""" + Environment.NewLine, requestStream);
                        WriteText(@"Content-Type: application/octet-stream" + Environment.NewLine + Environment.NewLine, requestStream);

                        WriteData(fileStream, requestStream);

                        WriteText(Environment.NewLine, requestStream);
                    }
                }
                // Output the final closing boundary
                WriteText("--" + boundary + "--" + Environment.NewLine, requestStream);
            }
            
            request.BeginGetResponse((getResponseResult) =>
                {
                    var response = request.EndGetResponse(getResponseResult);
                    string responseText = null;
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        responseText = reader.ReadToEnd();
                    }
                    onCompleted(responseText);
                }, null);
        }, files);
    }

    private static void WriteText(string input, Stream output)
    {
        var bytes = UTF8Encoding.UTF8.GetBytes(input);
        output.Write(bytes, 0, bytes.Length);
    }

    private static void WriteData(Stream input, Stream output)
    {
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = input.Read(buffer, 0, buffer.Length)) != 0)
        {
            output.Write(buffer, 0, bytesRead);
        }
    }
}

You can then use this helper in your code like so:

OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "All Files (*.*)|*.*";
ofd.Multiselect = true;
if (ofd.ShowDialog().GetValueOrDefault())
{
    HttpHelper.UploadFilesAsync(new Uri("ReadFiles.ashx", UriKind.Relative), ofd.Files,
        (responseText) =>
        {
            Console.WriteLine("Response was: " + responseText);
        });
}

For the ASP.NET code I added a generic handler called ReadFile.ashx and just as proof that it works, I wrote out the number of files that were uploaded.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class ReadFile : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/xml";
        context.Response.Write("<Result>" + context.Request.Files.Count + 
            " Files Uploaded</Result>");
    }
}

Hope this helps someone else out.

Enjoy!

Advertisements
  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: