Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / file

Transfer File Using WCF

4.83/5 (14 votes)
8 Jan 2015CPOL2 min read 77.8K   3.9K  
How to use WCF to stream large file size over TCP

Introduction

So you have a new task that ask to send some files to client once it become available from some application, the developer ask you to provide a web service so he can use it in his application, what the service do simply it take a file and push it the client server, the biggest problem you will face it in issues like this, is  sending a large file using HTTP

HTTP is not the best way considering it has not been built to this kind of work.  This Article trying solve this issue using WCF and TCP and streaming, in this artical will build the client and server at the end we will be able to send 75 MB file in less then 12 seconds. 

Using the code

Creating the receiver layer

The receiver will provide the sender with one simple method named ( Put) this method will take a FileTransferRequest object, as you can see in the code: 

C#
/// <summary>
/// Push File to the client
/// </summary>
/// <param name="fileToPush">file you want to Push</param>
/// <returns>a file transfer Response</returns>
public FileTransferResponse Put(FileTransferRequest fileToPush)
{
    FileTransferResponse fileTransferResponse = this.CheckFileTransferRequest(fileToPush);
    if(fileTransferResponse.ResponseStatus == "FileIsValed")
    {
        try
        {
            this.SaveFileStream(System.Configuration.ConfigurationManager.AppSettings["SavedLocation"].ToString() + "\\" + fileToPush.FileName, new MemoryStream(fileToPush.Content));
            return new FileTransferResponse
            {
                CreateAt = DateTime.Now,
                FileName = fileToPush.FileName,
                Message = "File was transfered",
                ResponseStatus = "Successful"
            };
        }
        catch (Exception ex)
        {
            return new FileTransferResponse
            {
                CreateAt = DateTime.Now,
                FileName = fileToPush.FileName,
                Message = ex.Message,
                ResponseStatus = "Error"
            };
        }
    }

    return fileTransferResponse;
}

The method ( Put ) will take a FileTransferRequest object and it will call the method SaveFileStream witch will talk the file content as byte and converted into stream then save it to given folder path , here's how the FileTransferRequest look like: 

C#
namespace FileTransferFramework.Client
{
    using System;
    using System.IO;
    using System.Runtime.Serialization;
    using System.ServiceModel;

    [DataContract]
    /// <summary>
    /// Transfer Request Object
    /// </summary>
    public class FileTransferRequest
    {
        [DataMember]
        /// <summary>
        /// Gets or sets File Name
        /// </summary>
        public string FileName { get; set; }

        [DataMember]
        public byte[] Content { get; set; }
    }
}

Before I call the SaveStream method I'm just make a simple check that the content file is not null and the name is set:

C#
/// <summary>
/// Check From file Transfer Object is not null
/// and all properties is set
/// </summary>
/// <param name="fileToPush">file to check</param>
/// <returns>File Transfer Response</returns>
private FileTransferResponse CheckFileTransferRequest(FileTransferRequest fileToPush)
{
    if (fileToPush != null)
    {
        if (!string.IsNullOrEmpty(fileToPush.FileName))
        {
            if (fileToPush.Content !=null)
            {
                return new FileTransferResponse
                {
                    CreateAt = DateTime.Now,
                    FileName = fileToPush.FileName,
                    Message = string.Empty,
                    ResponseStatus = "FileIsValed"
                };
            }

            return new FileTransferResponse
            {
                CreateAt = DateTime.Now,
                FileName = "No Name",
                Message = " File Content is null",
                ResponseStatus = "Error"
            };
        }

        return new FileTransferResponse
        {
            CreateAt = DateTime.Now,
            FileName = "No Name",
            Message = " File Name Can't be Null",
            ResponseStatus = "Error"
        };
    }

    return new FileTransferResponse
    {
        CreateAt = DateTime.Now,
        FileName = "No Name",
        Message = " File Can't be Null",
        ResponseStatus = "Error"
    };
}

I return a FileTransferResponse to tell the Put method that file is valed, so it can start wrting:

C#
/// <summary>
/// Write the Stream in the hard drive
/// </summary>
/// <param name="filePath">path to write the file in</param>
/// <param name="stream">stream to write</param>
private void SaveFileStream(string filePath, Stream stream)
{
    try
    {
        if(File.Exists(filePath))
        {
            File.Delete(filePath);
        }

        var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
        stream.CopyTo(fileStream);
        fileStream.Dispose();
    }
    catch(Exception ex)
    {
        throw ex;
    }
}

Note's the FileTransfer is inherited from IFileTransfer, as you can see: 

C#
namespace FileTransferFramework.Client
{
    using System.IO;
    using System.ServiceModel;

    [ServiceContract]
    public interface IFileTransfer
    {
        [OperationContract]
        FileTransferResponse Put(FileTransferRequest fileToPush);
    }
}

Now let's Update the app.onfig file to accept large file 

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="SavedLocation" value="C:\Users\aghabban\Documents\Visual Studio 2013\Projects\FileTransferFramework\Client\FileTransferFramework.Client\bin\Debug\Data\"/>
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="StreamServiceBehavior">
          <serviceMetadata httpGetEnabled="True" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <netTcpBinding>
        <binding name="NewBinding0" transferMode="Streamed" transactionFlow="false"  transactionProtocol="OleTransactions"
              hostNameComparisonMode="StrongWildcard" listenBacklog="10"
              maxBufferPoolSize="79623599" maxBufferSize="4967295" maxConnections="10"
              maxReceivedMessageSize="78623599">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="79623599"
              maxBytesPerRead="4096" maxNameTableCharCount="79623599" />
          <reliableSession ordered="true"
              enabled="false" />
          <security mode="None">
            <transport clientCredentialType="None"/>
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="StreamServiceBehavior" name="FileTransferFramework.Client.FileTransfer">
        <endpoint address="net.tcp://localhost:3021/streamserver" binding="netTcpBinding"
          bindingConfiguration="NewBinding0" bindingName="" contract="FileTransferFramework.Client.IFileTransfer" />
        <endpoint address="mex" binding="mexHttpBinding"
          contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:5210/StreamService" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>

Now the main method in Program class  will start the service

C#
static void Main(string[] args)
   {
       try
       {
           IntPtr hwnd;
           hwnd = GetConsoleWindow();
           ShowWindow(hwnd, SW_HIDE);

           using (var serviceHost = new ServiceHost(typeof(FileTransfer)))
           {
               serviceHost.Open();
               Console.WriteLine("Client Started..");
               Console.ReadKey();
           }
       }
       catch (Exception ex)
       {
           Console.WriteLine(ex.ToString());
           Console.ReadKey();
       }
   }

Now the receiver is ready to and wating for the sender, let's build the receiver. 

Building the sender 

First the sender will use the receiver wcf that we build, it locat in the code example at http://localhost:5201/StreamService so I will add it from the service references dialog, as the task ask me to do " is take what ever file you see in some folder and push it to the client ", so what I will do is using File watcher control in order to watch that folder and fire create file event when a new file created, 

C#
/// <summary>
/// main Program
/// </summary>
/// <param name="args">args of the file</param>
static void Main(string[] args)
{
    try
    {
        IntPtr hwnd;
        hwnd = GetConsoleWindow();
        ShowWindow(hwnd, SW_HIDE);

        FileSystemWatcher fileWatcher = new FileSystemWatcher(System.Configuration.ConfigurationManager.AppSettings["FolderToWatch"].ToString());
        fileWatcher.Created += FileWatcher_Created;
        fileWatcher.EnableRaisingEvents = true;
        Console.WriteLine("Watcher Started...");
    }
    catch (Exception ex)
    {
        Console.Write(ex.Message);
        new Logger().Create("Service not started", DateTime.Now, "Error", ex.Message);
    }

    Console.ReadKey();
}

In the FileWatcher_Created file I will take what ever file created and call the put method in the receiver service pass a FileTransferRequest as you can see:

C#
/// <summary>
/// File Watcher when Created
/// </summary>
/// <param name="sender">sender object</param>
/// <param name="e">e object</param>
private static void FileWatcher_Created(object sender, FileSystemEventArgs e)
{
    FileTransferResponse response = null;
    try
    {
        if (IsFileLocked(e.FullPath) == false)
        {
            DateTime startAt = DateTime.Now;
            FileTransferRequest createdFile = new FileTransferRequest()
            {
                FileName = e.Name,
                Content = File.ReadAllBytes(e.FullPath)
            };

            response = new FileTransferClient().Put(createdFile);

            if (response.ResponseStatus != "Successful")
            {
                MoveToFailedFolder(e);
            }
            else
            {
                if (File.Exists(e.FullPath))
                {
                    File.Delete(e.FullPath);
                }
            }

            Console.WriteLine(response.ResponseStatus + " at: " + DateTime.Now.Subtract(startAt).ToString());
            new Logger().Create(e.Name, DateTime.Now, response.ResponseStatus, response.Message);
        }
    }
    catch (System.ServiceModel.CommunicationException ex)
    {
        MoveToFailedFolder(e);
        Console.WriteLine(ex.Message);
        new Logger().Create(e.Name, DateTime.Now, "Error", ex.Message);
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
        if(response != null)
        {
            new Logger().Create(e.Name, DateTime.Now, response.ResponseStatus, response.Message);
        }
        else
        {
            new Logger().Create(e.Name, DateTime.Now, "Error", ex.Message);
        }
    }
}

Finally you need to steup the config file of the receiver  

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="DbConnection" value="Data Source=10.10.5.133;Initial Catalog=FileTransferFrwork;Integrated Security=True"/>
    <add key="FolderToWatch" value="C:\Users\aghabban\Documents\Visual Studio 2013\Projects\FileTransferFramework\Server\FileTrainsferFramework.Server\bin\Debug\Data\"/>
  </appSettings>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <system.serviceModel>     
        <bindings>
            <netTcpBinding>
                <binding name="NetTcpBinding_IFileTransfer" transferMode="Streamed"                     
              transactionFlow="false"  transactionProtocol="OleTransactions"
              hostNameComparisonMode="StrongWildcard" listenBacklog="10"
              maxBufferPoolSize="79623599" maxBufferSize="4967295" maxConnections="10"
              maxReceivedMessageSize="79623599">
                  <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="555555555"
                      maxBytesPerRead="4096" maxNameTableCharCount="79623599" />
                  <reliableSession ordered="true" 
                      enabled="false" />
                  <security mode="None">
                    <message clientCredentialType="None"/>
                    <transport clientCredentialType="None"/>
                  </security>
                </binding>              
            </netTcpBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://192.168.1.108:3021/streamserver" binding="netTcpBinding"
                bindingConfiguration="NetTcpBinding_IFileTransfer" contract="ClientFileTransferServiceReference.IFileTransfer"
                name="NetTcpBinding_IFileTransfer">               
            </endpoint>
        </client>      
    </system.serviceModel>
</configuration>

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)