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);
}


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


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.

27 thoughts on “Uploading with Silveright 2 Beta 1 and WCF

  1. 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

  2. Hello Lixin,

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

    For example:

    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 …

  3. 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);

  4. 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).

  5. Hi
    I am new to WCF and have looked at your example. But I have the following
    problem when I start your solution to debugg the code (to understand what
    happends).
    Since Client bin is not included in the project I have to include it and then
    add existing file (TestPage.html).
    I then set this as start page and run it. I select a file but when I hit add it
    crashes on the following line:
    System.IAsyncResult _result = base.BeginInvoke(“SaveFile”, _args, callback,
    asyncState);
    In the method BeginSaveFile(…).

    I get the error;
    An exception of type ‘System.ServiceModel.CommunicationException’ occurred in
    System.ServiceModel.dll but was not handled in user code

    Additional information: [CrossDomainError]
    Arguments:
    Debugging resource strings are unavailable. Often the key and arguments provide
    sufficient information to diagnose the problem. See
    http://go.microsoft.com/fwlink/?linkid=106663&Version=2.0.30226.2&File=System.ServiceModel.dll&Key=CrossDomainError

    Can you see what I am doing wrong? I guess including the Test.html file is
    where Im doing someting wrong.
    I really hope you can help me!!
    Arnt

  6. I think you are getting the cross-domain error. Please use the static port in the ASP.NET project. update that static port in Service Reference… then, it will work. please let me know if you are not clear.

  7. Hello,
    I need to upload files that have more than 50 Mo, but when I try to set the MaxBufferSize, MaxReceivedMessageSize, maxArrayLength and maxStringContentLength to 50000000, I get the 404 error.
    I hope that you can help me
    Thanks in advance

  8. quote: “I need to upload files that have more than 50 Mo, but when I try to set the MaxBufferSize, MaxReceivedMessageSize, maxArrayLength and maxStringContentLength to 50000000, I get the 404 error.”

    check maxRequestLength too.,
    i also remember reading something about IIS server limits, but dont remember if it 1 Gb or smaller. Check with MS. IIS limit can be increased by doing registry edit. That required to restart physicall server.

  9. Hi Michael,

    I’m getting the same problem as Arnt, and i’m using static port 50205 but i still get the same error

    “An error occurred while trying to make a request to URI ‘http://localhost:50205/Upload.svc’. This could be due to a cross domain configuration error. Please see the inner exception for more details”

    do you know what im doing wrong?

    Thanks in advance

    Sunny d

  10. there is a service url in wsdl file. you can check whether you are using correct port or not..

    OR,

    YOu should try to update the service url in Silverlight application….

  11. Do you have an update for Silverlight 2 Beta 2? I have been trying to upload files from 4MB to 100’sMB with no luck.

    Also is the ServiceReferences.ClientConfig file being used in Beta 2? Would you need to set the readerQuotas in the file because it does not accept the element?

  12. I did;
    web.config

    ServiceReferences.ClientConfig

    Then I am setting the values in the application calling the service
    _binding.MaxBufferSize = 2147483647;
    _binding.MaxReceivedMessageSize = 2147483647;

  13. previous post stripped the xml

    web.config

    binding name=”ServicesBinding” maxReceivedMessageSize=”2147483647″ maxBufferSize=”2147483647″
    readerQuotas maxDepth=”2147483647″ maxStringContentLength=”2147483647″ maxArrayLength=”2147483647″ maxBytesPerRead=”2147483647″ maxNameTableCharCount=”2147483647″

    ServiceReferences.ClientConfig

    binding name=”BasicHttpBinding_UploadService” maxBufferSize=”2147483647″ maxReceivedMessageSize=”2147483647″

  14. I had also some file size issues. Besides the changes in MaxBufferSize and MaxReceivedMessageSize, I added in the server web.config:

    In the tag :

  15. I had also some file size issues. Besides the changes in MaxBufferSize and MaxReceivedMessageSize, I added in the server web.config:

    In the tag _system.web_:
    (2097151 = 2GB This is the maximum value allowed)
    _httpRuntime maxRequestLength=”2097151″ /_

  16. Just wanted to say thank you. This article really helped me save a lot of time and the few pieces of hair I have left. I had the size limit and the 404 issues that this article addressed perfectly!

  17. Sunny and Arnt fix the cross domain error… do this.

    1. Keep the reference as it is, no need to use local port
    2. Go to your webroot (mine is C:\inetpub\wwwroot\)
    3. Create a xml file: crossdomain.xml here with following contents:

    Now the upload should work….

  18. Very nice sample project. It help a lot. I just added clientaccesspolicy.xml to WCF service project and key value of FileUploadLocation in web.config changed to value=”/UploadedFiles/”.

    It is working fine for me.
    Thanks a lot.

  19. ive tried ur solution but have errors..

    ‘System.Windows.Controls.OpenFileDialog’ does not contain a definition for ‘SelectedFile’ and no extension method ‘SelectedFile’ accepting a first argument of type ‘System.Windows.Controls.OpenFileDialog’ could be found (are you missing a using directive or an assembly reference?) D:\Upload\SilverlightClient\Page.xaml.cs

    ‘System.Windows.Controls.DialogResult’ is inaccessible due to its protection level D:\Upload\SilverlightClient\Page.xaml.cs

    ‘System.Windows.Controls.OpenFileDialog’ does not contain a definition for ‘EnableMultipleSelection’ and no extension method ‘EnableMultipleSelection’ accepting a first argument of type ‘System.Windows.Controls.OpenFileDialog’ could be found (are you missing a using directive or an assembly reference?) D:\Upload\SilverlightClient\Page.xaml.cs

    The name ‘DialogResult’ does not exist in the current context D:\Upload\SilverlightClient\Page.xaml.cs

    ‘System.Windows.Controls.OpenFileDialog’ does not contain a definition for ‘Dispose’ and no extension method ‘Dispose’ accepting a first argument of type ‘System.Windows.Controls.OpenFileDialog’ could be found (are you missing a using directive or an assembly reference?) D:\Upload\SilverlightClient\Page.xaml.cs

  20. the above blog mentioned 2 config
    1. web.config
    2. ServiceReferences.ClientConfig (we have it in XAML silverligt)

    where web.config will be available
    it is WCF web.config or how?

Leave a Reply

Your email address will not be published. Required fields are marked *