Click here to Skip to main content
Click here to Skip to main content

Progress Indication while Uploading/Downloading Files using WCF

By , 4 Feb 2009
 
Screenshot - WCF_FileTransfer_Progress.png

Introduction

This article examines the implementation of upload and download functionality with progress indication (progress bar feature) using the Windows Communication Foundation. For this sample, you will need Visual Studio 2008.

The sample code consists of three projects bundled in a solution. A brief description of these projects follows. The attached sample code is available in C# and VB.NET (conversion to VB.NET was made by Lee Galloway of Project Time and Cost).

FileService

This is the main server project.

The Server Contract

The File Server project includes FileTransferServiceContract.cs file, which contains the IFileTransferService interface. This interface describes the operations provided by our server. No actual work is done in this code file except in describing the operations provided. If you've worked with service-oriented applications before, you'll know that this job is important enough to spare a separate file for. Here are the two operations of our file transfer service:

  • DownloadFile Server Method

    Accepts a DownloadRequest instance that contains the name of the file to be downloaded by the client. It returns a RemoteFileInfo instance, defined in the same code file. RemoteFileInfo contains the name of the file to be downloaded, the file stream and the length of the file in bytes. This instance of the RemoteFileInfo class will be used by the client to download the file. You notice that filename and length are marked with the MessageHeader attribute in the RemoteFileInfo class. This is because when a message contract contains a stream, this can be the only body member of the contract.

  • UploadFile Server Method

    Accepts an instance of the RemoteFileInfo message contract. This is the same as used in DownloadFile, only in this case the length property is not required.

CS

[ServiceContract()]
public interface IFileTransferService
{
    [OperationContract()]
    void UploadFile(RemoteFileInfo request);

    [OperationContract]
    RemoteFileInfo DownloadFile(DownloadRequest request);
}

public class RemoteFileInfo
{
    [MessageHeader(MustUnderstand = true)]
    public string FileName;

    [MessageHeader(MustUnderstand = true)]
    public long Length;

    [MessageBodyMember(Order = 1)]
    public System.IO.Stream FileByteStream;
}

VB

<ServiceContract()> _
<servicecontract() />Public Interface IFileTransferService

    <OperationContract()> _
    Sub UploadFile(ByVal request As RemoteFileInfo)

    <OperationContract()> _
    Function DownloadFile(ByVal request As DownloadRequest) As RemoteFileInfo

End Interface

<MessageContract()> _
Public Class RemoteFileInfo
    Implements IDisposable

    <OperationContract()> _
    Public FileName As String

    <OperationContract()> _
    Public Length As Long

    <MessageBodyMember(Order:=1)> _
    Public FileByteStream As System.IO.Stream

End Class

The Server Implementation

File Server also includes the FileTransferService.cs code file which contains the implementation of the contract, i.e. the actual code that does all the work. Apparently the included class implements the IFileTransferService class, which constitutes the service contract. If you have worked with streams before in .NET, you will find out that the code that handles the stream and related information for upload or download is pretty straightforward. If you are new to .NET streams, please use Google for a quick introduction.

Note here that since actual downloading of the file starts after the execution of the DownloadFile method is completed (i.e. after the client gets the RemoteFileInfo instance returned by this method), the server must close the opened stream later, after the client completes the process. An elegant approach was suggested by Buddhike. To do this, the IDisposable interface is implemented by the RemoteFileInfo contract and the stream is disposed on the corresponding Dispose method. If this is not done, the stream will remain locked and the corresponding file will be locked for writing.

ConsoleHost

FileService is a class library and hence it cannot start as a window process. Therefore it needs another executable file-process that will host it. Several types of processes can host a WCF service, such as .NET executables, IIS processes, Windows Activation Services (new feature of Vista) and many more. Our example uses a .NET executable to host our service. So, ConsoleHost is a console application that does exactly this. It has a reference to the FileService project. However, it is not related in any way with the business our service is doing, i.e. transferring files. Actually, the code you will find in Program.cs would be the same even if our service was designed to host an online grocery. Take a quick look at this code file to understand how our service is started and closed.

CS

static void Main(string[] args)
{
    ServiceHost myServiceHost = new ServiceHost
                (typeof(FileService.FileTransferService));
    myServiceHost.Open();

    Console.WriteLine("This is the SERVER console");
    Console.WriteLine("Service Started!");
    foreach (Uri address in myServiceHost.BaseAddresses)
        Console.WriteLine("Listening on " + address.ToString());
    Console.WriteLine("Click any key to close...");
    Console.ReadKey();

    myServiceHost.Close();
}

VB

Public Shared Sub Main()

    Dim myServiceHost As New ServiceHost(_
        GetType(FileService.FileTransferService))
    myServiceHost.Open()

    Console.WriteLine("This is the SERVER console")
    Console.WriteLine("Service Started!")
    For Each address As Uri In myServiceHost.BaseAddresses
        Console.WriteLine("Listening on " + address.ToString())
    Next
    Console.WriteLine("Click any key to close...")
    Console.ReadKey()

    myServiceHost.Close()

End Sub

The configuration of ConsoleHost is what matters the most! It is divided into three sections, configuring the way our service will behave and how it will be exposed to the rest of the world. It is not the goal of this article to describe in detail how a WCF service is configured, so please refer to the WCF reference on MSDN for more information. Something noticeable in the configuration of our service is that it uses MTOM as message encoding and stream as transfer mode. See also the maxReceivedMessageSize property. This defines the maximum size of messages transferred by our service. Since we are transferring files, we want this property to have a large value.

XML

<binding name ="FileTransferServicesBinding"
    transferMode="Streamed"
    messageEncoding="Mtom"
    maxReceivedMessageSize="10067108864" >
</binding>

Client

The Client project is a sample consumer of our service. You will notice that the Client project includes a folder called Service References. This folder contains a bunch of files created automatically by Visual Studio by right clicking on the Client project root and selecting "Add Service Reference." The files in this folder are the proxy of our file transfer service on the client side. Client is using these files to send requests to the server, hiding in this way the complexity of Web Service and SOAP protocols.

Again, if you have worked with streams before, you will notice that things are pretty simple in the TestForm file except for one small part, which is also the difference in implementing the progress indication when uploading rather than when downloading. When downloading, the client has control of the procedure. You can see in TestForm.cs that downloading is implemented using a loop that reads the server stream piece-by-piece. So, the client knows what part of the server stream is read and how many more remain. When uploading, that loop resides on the server. In order for the client to know how many bytes the server read, it uses the StreamWithProgress class, which inherits System.IO.Stream. An instance of this class is passed to the server, instead of the original file stream. Since this class overrides the default Read method of the stream (see code below), it can report the progress of the uploading process to the client!

CS

public override
    int Read(byte[] buffer, int offset,
    int count)
{
    int result = file.Read(buffer, offset, count);
    bytesRead += result;
    if (ProgressChanged != null) 
    ProgressChanged(this, new ProgressChangedEventArgs(bytesRead, length));
    return result;
}

VB

Public Overloads Overrides Function Read(ByVal buffer As Byte(), _
    ByVal offset As Integer, ByVal count As Integer) As Integer
    Dim result As Integer = file.Read(buffer, offset, count)
    bytesRead += result
    RaiseEvent ProgressChanged(Me, New ProgressChangedEventArgs(_
        bytesRead, m_length))
    Return result
End Function

History

  • Updated on 2007/09/09: A more elegant approach for the server implementation was suggested by Buddhike.
  • Updated on 2007/10/24: Code now also provided in VB.NET. Conversion made by Lee Galloway of Project Time and Cost.
  • Updated on 2009/02/03:
    • Upgraded projects to Visual Studio 2008 (.NET Framework 2.0 is still the target framework).
    • Properly closing read stream on client when download is done. Failure to do this was causing the client to throw timeout exceptions after 2 or 3 downloads.

License

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

About the Author

Dimitris Papadimitriou
Software Developer (Senior)
Luxembourg Luxembourg
Member
Dimitris Papadimitriou is a Software Development Professional specialized in Microsoft technologies. He has been awarded with the Microsoft MVP award (Connected System Developer). He lives in Luxembourg. Read more in his personal web page.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHow use svcutil.exe FileTransferClient.map file?membermarkmao_love16 Oct '12 - 16:55 
How use svcutil.exe Generating proxy code FileTransferClient.map and FileTransferClient.cs file?
GeneralMy vote of 5memberMember 372567114 Oct '12 - 20:43 
clear code
GeneralMy vote of 5memberterrykaoru3 Jul '12 - 22:53 
Your example is great! Thank you!
QuestionMy vote of 5!memberchemark2 Aug '11 - 20:05 
Very nice and useful!
chemark

QuestionFileByteStream.Close() throws an exceptionmemberemail.condivisa@gmail.com28 Jul '11 - 5:05 
If the transfer fails the FileByteStream.Close() throws an exception
 
public void Dispose()
{
// close stream when the contract instance is disposed. this ensures that stream is closed when file download is complete, since download procedure is handled by the client and the stream must be closed on server.
// thanks Bhuddhike! http://blogs.thinktecture.com/buddhike/archive/2007/09/06/414936.aspx
 
try // My corretion
{ // My corretion
if (FileByteStream != null)
{
FileByteStream.Close(); // throw an exception
FileByteStream = null;
}
}// My corretion
catch (Exception ex) // My corretion
{
FileByteStream = null;// My corretion
} // My corretion
}
AnswerRe: FileByteStream.Close() throws an exceptionmemberDimitris Papadimitriou28 Jul '11 - 5:55 
Can you tell me what is the exception?
Have you tried any solution yet?
 
Dimitris Papadimitriou
Software Development Professional
Microsoft MVP
GeneralRe: FileByteStream.Close() throws an exceptionmemberemail.condivisa@gmail.com28 Jul '11 - 22:57 
hello your solution is great!!
i tried to move a big file (500 Mb) and i tried to move also the form in debug. In this case the application didn't respond and it finished with timeout exception. The Client Application took the correct error but the stream was closed before the dispose method call .
 
Do you know how increase the timeout?
 
Thnaks
LS
GeneralRe: FileByteStream.Close() throws an exceptionmemberkiquenet AE4 Nov '11 - 6:18 
any solution about it ? thanks
AE

QuestionDoes this work with Mtom, Streaming, basicHttpBinding, hosted on IIS ?memberfreddie200021 Jun '11 - 4:16 
I used your client, service code exactly as is (VisualStudio 2010); but hosted on IIS; Got it to work when messageEncoding was Text and not Mtom; But with Text encoding, the problem was no matter what i did with the config files on the client and the service, and changing client chunk size, could not get the stream to read more than 1536 bytes (Problem A) !! So I changed encoding to Mtom and it complains server expects text/xml, even though i created a services node in the config file (Problem B); Give it a try if you feel like a challenge, but i could not get past either problem.
QuestionCompliment and question about streamsmembergerardo_00110 Nov '10 - 18:46 
Thank you for this article. It is very clear and shows a lot about WCF. I am trying to implement encryption by combining the streamwithprogressstream with cryptostream, but I have a question: Would the data be encrypted before it gets to the server or after? or "it depends".
Thank you.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 4 Feb 2009
Article Copyright 2007 by Dimitris Papadimitriou
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid