Uploading with Silveright 2 Beta 1 and WCF

By: Brad McKelvie

SourceCode Download : Upload.zip

This is a guest post by Brad McKelvie. (Thank a lot, Brad!) If you like to share some tips/tricks, articles or tutorials, please let me know.

This method allows the upload of any file type to a WCF Service which then stores it on the server. While researching uploading I came across the following Silverlight 2 Beta 1 limitations that led to my solution:

BassicHttpBinding.TransferMode property is not accessible to Stream data
BassicHttpBinding.MessageEncoding property is not accessible to set it as MTOM

Due to the above I elected to convert a Stream into a byte[] array, and then send it over to the Service.

By default the largest message that can be sent to a service from the client is 8124 bytes, and the largest message that can be received on the WCF service is 65536. Changing these settings and how to get this all working is explained below.

Creating the WCF Service to consume uploaded files

1. Create a new WCF Service. I have named it, Upload.svc.

2. Create a DataContract for the file being sent to the server. In this Contract the file’s name needs to also be sent if you want to keep the original name when saving to the server’s file system.

[DataContract]
public class FileUpload
{
[DataMember]
public string Name;

[DataMember]
public byte[] File;
}

3. Create an Interface for the Service in IUpload.cs and add an OperationContract for the StoreFile function.

[ServiceContract]
public interface IUpload
{
[OperationContract]
void StoreFile(FileUpload UploadedFile);
}
1

4. Create the Service function that consumes an uploaded file and stores it to the file system

1
public void StoreFile(FileUpload UploadedFile)
{

FileStream FileStream = new FileStream(HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["FileUploadLocation"]) + UploadedFile.Name, FileMode.Create);
FileStream.Write(UploadedFile.File, 0, UploadedFile.File.Length);

FileStream.Close();
FileStream.Dispose();
}

5. Setup your Web.Config to accept files bigger than 65536 bytes. For this example I have a size of 2 MB. To be safe I set all the properties to 2 MB as I didn’t want to run into any problems later.

Edit the binding configuration for BasicHttpBinding:

<bindings>
<basicHttpBinding>
<binding name="ServicesBinding" maxReceivedMessageSize="2000000" maxBufferSize="2000000">
<readerQuotas maxArrayLength="2000000" maxStringContentLength="2000000"/>
</binding>
</basicHttpBinding>
</bindings>

Tell your Binding to use this new configuration with the property, bindingConfiguration:

<endpoint address="" bindingConfiguration="ServicesBinding" binding="basicHttpBinding" contract="WCFServices.IUpload">

Call the Upload Service on the Silverlight Client

A user must select a file using the OpenFileDialog Control in order to gain read access to a file on the Client. Once Silverlight has the ability to read a file, then the file’s Stream is converted into a byte[] array. Finally, the byte[] array and file name are sent to the WCF Service.

1. Add a Service Reference to your Silverlight project. I called mine, UploadServiceReference.

2. Create the WCF Client that calls your Upload Service

UploadServiceReference.UploadClient UploadServiceClient = null;

System.ServiceModel.BasicHttpBinding Binding = new BasicHttpBinding(BasicHttpSecurityMode.None);

Binding.MaxBufferSize = 2000000;
Binding.MaxReceivedMessageSize = 2000000;

EndpointAddress Address = new EndpointAddress("http://localhost/MyWCFServices/Upload.svc");

UploadServiceClient = new UploadServiceReference.UploadClient(Binding, Address);

Note: The BasicHttpBinding’s MaxBufferSize must be changed if you want to upload a file bigger than 8124 bytes. The default is low to help defend against DOS attacks. In this example I have set the maximum upload size of the file to 2 MB. This setting MUST be set in the code because Silverlight 2 Beta 1 does not read the settings from the ServiceReferences.ClientConfig file.

When I didn’t have the MaxBufferSize set I would receive a 404 error. This error of course made no sense. I had to download the WCF Service Configuration Utility that is included in the Windows SDK. After enabling the log of my WCF Service sent/received messages I found that the real error was: “The maximum string content length quota (8192) has been exceeded while reading XML data. This quota may be increased by changing the MaxStringContentLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader.”

3. Create a function to call the WCF Service and upload a file after a file has been selected by a user with the OpenFileDialog Control. Below is the Event that is attached to a Button control. When the Button is clicked it uploads the file.

private void AddFile_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Stream Stream = (System.IO.Stream)UploadOpenFileDialog.SelectedFile.OpenRead();

byte[] Buffer = new byte[Stream.Length];
Stream.Read(Buffer, 0, (int)Stream.Length);

Stream.Dispose();
Stream.Close();

UploadServiceReference.FileUpload FileUpload = new UploadServiceReference. FileUpload();
FileUpload.FileName = UploadOpenFileDialog.SelectedFile.Name;
FileUpload.File = Buffer;

UploadServiceClient.StoreFileAsync(FileUpload);
UploadServiceClient.StoreFileCompleted += new EventHandler<Upload. UploadServiceReference.StoreFileCompletedEventArgs>(StoreFile_Completed);
}

Additional Items that will help

I had a need to use Server.MapPath when dealing with storing files. To enable HttpContext.Current.Server.MapPath:

Add the line:


[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]

Above your Service class:


public class Upload : IUpload

Then in your Web.config add the line:


<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

To:


<system.serviceModel></system.serviceModel>

Note: The sourcecode will be uploaded very soon. The sourcecode is uploaded. Thanks for reading.