Sending Binary Files Over WCF

October 16, 2015

This is an interesting one - it is possible to load a binary file (such as an exe) in a .NET service and return it to the client via WCF. It’s actually not that complex either; in this example, I’ve created a basic service and console application.

Code

The code for the service is really straight forward:



    public class Service1 : IService1
    {
        public Stream GetData(int value)
        {
            FileStream stream = File.OpenRead(@"c:\\tmp\\MyFile");
            return stream;
        }
    }

And here is the Interface:



    [ServiceContract]
    public interface IService1
    {

        [OperationContract]
        Stream GetData(int value);
    }

The important part about the above code is that the method must return a Stream, and can only accept a single parameter (you can send large data by reversing this).

The client code is a little more involved. The Main() function of the console app is here:



        static void Main(string[] args)
        {
            Task rd = ReceiveData();
            rd.Wait();

And ReceiveData() looks like this:



        private static async Task ReceiveData()
        {
            ServiceReference1.Service1Client sc = new ServiceReference1.Service1Client();
            Stream stream = await sc.GetDataAsync();            

            using (var fs = File.Create(@"c:\\tmp2\\newfile.exe"))
            {
                int b;
                do
                {
                    b = stream.ReadByte();
                    fs.WriteByte((byte)b); 
                } while (b != -1);
            }
        }

As you can see, although the code is a bit messy, it’s not complex, and the bulk of the code is actually turning the Stream into a file (I’m sure there’s an easier way of doing this).

Configuration

The config files are the key here. The encoding and transfer mode need to be changed.

The service web.config:

    <bindings>
      <basicHttpBinding>
        <binding name="BasicStreaming" messageEncoding="Mtom" transferMode="Streamed"
                 closeTimeout="10:00:00"
                 />

      </basicHttpBinding>
    </bindings>

Create an endpoint for the service and bind it to above:

  <system.serviceModel>
    <services>
      <service name="FileService.Service1">
        <endpoint address="Service1.svc" binding="basicHttpBinding"
          bindingConfiguration="BasicStreaming" contract="FileService.IService1" />
      </service>
    </services>

So, the transfer mode is streamed, and the encoding is ‘Mtom’. Make sure that the endpoint is configured against the binding (otherwise it’ll use the default binding, repeatedly moan that it’s out of memory and mismatched for no apparent reason and you’ll spend ages wondering why).

The client config can be updated using the “Update Service Reference” option; however, double check what it adds.

The App.config changes from the client:

    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicStreaming" closeTimeout="10:00:00" 
                    maxReceivedMessageSize="400000000" messageEncoding="Mtom" />
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:17065/Service1.svc/Service1.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicStreaming"
                contract="ServiceReference1.IService1" name="BasicHttpBinding\_IService1" />
        </client>
    </system.serviceModel>

The only real thing of note in the client is the `maxReceivedMessageSize`; this has to be big enough to take your largest file. Obviously, the timeout settings matter, but maybe if you’re taking 10 minutes to transfer then you’ve got bigger problems.

The End

That’s it - it all works neatly, and looks deceptively easy!



Profile picture

A blog about one man's journey through code… and some pictures of the Peak District
Twitter

© Paul Michaels 2024