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);
}
[sourcecode]

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

[sourcecode language="csharp"]
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.

7 Comments so far »

  1. Wöchentliche Rundablage: WPF, Silverlight 2, ASP.NET MVC, .NET 3.5… | Code-Inside Blog said

    am March 31 2008 @ 12:03 pm

    [...] Uploading with Silveright 2 Beta 1 and WCF [...]

  2. Michael Sync » Uploading with Silveright 2 Beta 1 and ASMX Web Service said

    am April 2 2008 @ 8:32 am

    [...] with Silverlight 2 (beta1). As some of you might notice that Brad McKelvie wrote an article about how to upload the file with WCF and Silverlight 2 (beta1) in my blog recently. Now, egoZd shares his idea to use ASMX web service instead of WCF for [...]

  3. Lixin said

    am April 5 2008 @ 4:51 am

    Hi Michael,

    Is there an actual limit on file size in this uploading approach?

    I mean Silverlight + WCF Service or Silverlight + asmx Service

    If there is, is that possible to increase that limit?

    Thanks

    Lixin

  4. Michael Sync said

    am April 6 2008 @ 4:09 am

    Hello Lixin,

    Yes. For ASMX web service, you can change the max size in web.config.

    For example:

    maxRequestLength="4096"
    useFullyQualifiedRedirectUrl="false"
    minFreeThreads="8"
    minLocalRequestFreeThreads="4"
    appRequestQueueLimit="100"
    />

    By default, ASP.NET permits only files that are 4,096 kilobytes (KB) (or 4 megabytes [MB]) or less to be uploaded to the Web server. To upload larger files, you must change the maxRequestLength parameter of the section in the Web.config file.

    For WCF, you can change MaxBufferSize and MaxReceivedSize …

  5. Shaun Venus said

    am April 18 2008 @ 8:32 am

    I downloaded your code and found the the SaveFileCompleted handler is being set after the async call is being made. Should the lines of code (bottom of the Add_MouseLeftButtonUp method in Page.xaml.cs) be in this order:

    UploadClient.SaveFileCompleted += new EventHandler(UploadClient_SaveFileCompleted);

    UploadClient.SaveFileAsync(UploadFile);

  6. Michael Sync said

    am April 18 2008 @ 9:04 am

    Yes. It would be better but I think both ways will be working fine.. let me know if it’s not working for you..

  7. Jeremy Thake said

    am May 14 2008 @ 11:06 pm

    I am using WCF services and can’t see a way of setting a behavoiur on the client as you would in a config file e.g.:

    behavior name=”XamlPrintWcfService.XamlPrintWcfIServiceBehavior”
    dataContractSerializer maxItemsInObjectGraph=
    “6553600″

    How can this be done in C# code within the Silverlight Project. Note I’m using Silverlight 2.0 beta 1. I noticed that is does create the ServiceReferences.ClientConfig but it simply doesn’t use this and you have to set it in code using your above example for WCF (you state it works for ASMX).

Comment RSS · TrackBack URI

Leave a comment

Name: (Required)

eMail: (Required)

Website:

Comment: