File Download and Upload with Progress using WCF

No.of Views6279
Bookmarked8 times
Downloads 
Votes0
By  abhi2434   On  26 Nov 2010 07:11:10
Tag : WCF , Utilities
I like to use WCF as it is very easy to implement and also provides built in functions to handle with Complex problems. One of such interesting thing that I found in WCF is the support for Streaming while sending data from the service.
emailbookmarkadd commentsprint

Images in this article missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at info@codegain.com

 

Introduction

I like to use WCF as it is very easy to implement and also provides built in functions to handle with Complex problems. One of such interesting thing that I found in WCF is the support for Streaming while sending data from the service.

WCF allows you to send Byte streams through the communication channel or even allows you to implement a service that might take the stream start location from the service call and send the stream from a certain location. It allows you to use normal HttpBinding to support streams which is capable download or upload in connected Client - Server architecture. Hence there will be not timeouts for sending or receiving large files. In this article I will show you how you could use Streaming to ensure your files are downloaded or uploaded correctly.

Implementation

Lets first create the service step by step :

Service

1. Create a Service Application and name it as WCFCommunicationService
2. The first thing that you need to do in order to create a service is ServiceContract. So once you create a Service Library, you need to delete the existing Service1 and IService1 files from the solution and add a new Interface.
3. Create Two method, one for FileDownload and another for FileUpload. Lets see how the service definition looks like :

Contract

[ServiceContract]
public interface IFileTransferLibrary
{

    [OperationContract()]void UploadFile(ResponseFile request);

    [OperationContract]
    ResponseFile DownloadFile(RequestFile request);
}

 The Contract defines the methods that are exposed to outside. The ServiceContract defines the interface which will have the members which are available to outside. The OperationContract identifies the exposed members. In the above contract the IFileTransferLibrary has two methods, UploadFile which takes an object of ResponseFile and DownloadFile which returns an object of ResponseFile.

[MessageContract]
public class ResponseFile : IDisposable
{
    [MessageHeader]public string FileName;

    [MessageHeader]public long Length;

    [MessageBodyMember]public System.IO.Stream FileByteStream;

    [MessageHeader]public long byteStart = 0;public void Dispose()
    {if (FileByteStream != null)
        {
            FileByteStream.Close();
            FileByteStream = null;
        }
    }
}

[MessageContract]
public class RequestFile
{
    [MessageBodyMember]public string FileName;

    [MessageBodyMember]public long byteStart = 0;
}

If you see the classes RequestFile and ResponseFile, you will see that I have used MessageContract instead of DataContract for my complex types. It is important. AS I am going to send the data streamed to the client, I need to send the data into packets. DataContract will send the whole object at a time while Messagecontract sends them into small Messages.It is also important to note that I have used IDisposable for ResponseFile to ensure that the Stream is closed whenever the connection is terminated.

Another important note is you need to use Stream as MessageBodyMember. The stream data will always transferred in MessageBody and you cannot add any other member into it. Thus you can see I have put all other members as MessageHeader.

For Both UploadFile and DownloadFile the Stream is taken from the ResponseFile object. The byteStart element of RequestFile ensures from where the the downloading should start and hence helps in Resuming a half downloaded file.  Lets see how to implement this :

public ResponseFile DownloadFile(RequestFile request)
{
    ResponseFile result = new ResponseFile();

    FileStream stream = this.GetFileStream(Path.GetFullPath(request.FileName));
    stream.Seek(request.byteStart, SeekOrigin.Begin);
    result.FileName = request.FileName;
    result.Length = stream.Length;
    result.FileByteStream = stream;return result;

}
private FileStream GetFileStream(string filePath)
{
    FileInfo fileInfo = new FileInfo(filePath);if (!fileInfo.Exists)throw new FileNotFoundException("File not found");return new FileStream(filePath, FileMode.Open, FileAccess.Read);
}
public void UploadFile(ResponseFile request)
{string filePath = Path.GetFullPath(request.FileName);int chunkSize = 2048;byte[] buffer = new byte[chunkSize];using (FileStream stream = new FileStream(filePath, FileMode.Append, FileAccess.Write))
    {do{int readbyte = request.FileByteStream.Read(buffer, 0, chunkSize);if (readbyte == 0) break;

            stream.Write(buffer, 0, readbyte);
        } while (true);
        stream.Close();
    }
}

If you see the code, you can see how I have used byteStart to start sending Bytes for the file. This ensures the file download to have resume facility. You can also implement the same for UploadFile too.

ServerHost

After you create the Server application lets create the ServiceHost application. For the time being, I have used a Console application to demonstrate the thing. Lets add a new application to the solution and add the following lines in the Main method.

static void Main(string[] args)
{using (ServiceHost host = new ServiceHost(typeof(FileTransferLibrary)))
    {
        host.Open();
        Console.WriteLine("Service Started!");foreach (Uri address in host.BaseAddresses)
            Console.WriteLine("Listening on " + address);
        Console.WriteLine("Press any key to close...");
        Console.ReadKey();

        host.Close();
    }
}

 Here I have used ServiceHost to host the Service. If you are going to use a Windows Service which I would probably like for this situation, I would have been writing the same code as you see above and use host.Open during the OnStart of the Service and host.Close during the OnStop of the service.

Finally lets configure the application. The configuration settings that I am going to use for the service is BasicHttpBinding with MTOM support.Lets see how my ServiceModel section of the service looks like :
 

<system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="WCFCommunicationLibrary.FileTransferServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <basicHttpBinding>
        <binding name="FileTransferServicesBinding"
          transferMode="Streamed"
          messageEncoding="Mtom"
          sendTimeout="01:05:00"
          maxReceivedMessageSize="10067108864">
        </binding>
      </basicHttpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="WCFCommunicationLibrary.FileTransferServiceBehavior"
        name="WCFCommunicationLibrary.FileTransferLibrary">
        <clear />
        <endpoint address="http://localhost:8080/FileDownloaderLibrary/" binding="basicHttpBinding"
          bindingConfiguration="FileTransferServicesBinding" contract="WCFCommunicationLibrary.IFileTransferLibrary">
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange">
        </endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8080/FileDownloader/" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>

Just as I said, the service config is very simple with Streamed as TransferMode of ServiceBinding. I have used BasicHttpBinding for simplicity but you can easily use TCPBinding if you like to.The endpoint defines the address as http://localhost:8080/FileDownloaderLibrary/ the Binding as BasicHttpBinding and the contract with the WCFCommunicationLibrary.IFileTransferLibrary which is the basic contract for our service.

If you run the console you will see the service started running with the following message, which indicates that the service is available for Download or upload of Files.
 

Image Loading

You can host your Service class library anywhere you want. The probable hosting environment could be IIS, Windows Activation Service, Windows Service, .NET programs, etc.

Now lets create the client application to download/ upload the files to the server. Client is actually a consumer of the Service which lets you to add the service reference to the client and will produce the discovery files which helps you to access the Streamed services and also allows you to produce progress behaviour for the file. Lets create an application which has this functionality.

Image Loading

 

While creating the client, you need to follow the steps :

1. Add a service reference to the Client, so that you can call it. The Service Reference will automatically create the discovery files.
2. Once you added, design your UI, I have used WPF to design my UI,
3. Use server methods directly to store or send Stream or data.

public void StartDownloading()
{try{string filePath = System.IO.Path.Combine("Download", this.FileName);string fileName = this.FileName;

        Stream inputStream;long startlength = 0;
        FileInfo finfo = new FileInfo(filePath);if (finfo.Exists)
            startlength = finfo.Length; // If File exists we need to send the Start length to the serverlong length = this.Client.DownloadFile(ref fileName, ref startlength, out inputStream);using (FileStream writeStream = new System.IO.FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
        {int chunkSize = 2048;byte[] buffer = new byte[chunkSize];do{
    
       int bytesRead = inputStream.Read(buffer, 0, chunkSize);if (bytesRead == 0) break;

    
                writeStream.Write(buffer, 0, bytesRead);this.ProgressValue = (int)(writeStream.Position * 100 / length);
            }while (true);


            writeStream.Close();
        }

        inputStream.Dispose();
    }catch{

    }
}

The code allows you to save the file to the disk. You can see we can easily stop this operation by clicking on Pause Button. The notion of Pause button is actually to stop the Thread completely. Hence the operation will be stopped and the file will remain in the disk until it is downloaded. When you resume, the process is called again. As in the function we check the size of the file and send it back to the server, this will ensure that the server will start on sending the file from a specified point.

Download Sample Project

Download source files -160 kb

Summary

I like to use WCF as it is very easy to implement and also provides built in functions to handle with Complex problems. One of such interesting thing that I found in WCF is the support for Streaming while sending data from the service.

 
Sign Up to vote for this article
 
About Author
 
abhi2434
Occupation-Not Provided
Company-Not Provided
Member Type-Senior
Location-Not Provided
Joined date-22 Oct 2009
Home Page-Not Provided
Blog Page-Not Provided
 
 
Other popularSectionarticles
Comments
There is no comments for this articles.
Leave a Reply
Title:
Display Name:
Email:
(not display in page for the security purphase)
Website:
Message:
Please refresh your screen using Ctrl+F5
If you can't read this number refresh your screen
Please input the anti-spam code that you can read in the image.
^ Scroll to Top
</