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:
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:
namespace FileTransferFramework.Client
{
using System;
using System.IO;
using System.Runtime.Serialization;
using System.ServiceModel;
[DataContract]
public class FileTransferRequest
{
[DataMember]
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:
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:
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:
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
="1.0"="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
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,
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:
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
="1.0"="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>