Detecting Upload Progress with WCF

So as it turns out it’s not very hard to setup a WCF service so that you can watch the progress of the file uploads to the service. I only found one post online that provided a good demonstration of how to do it and I am going to go over it in again with a few additional notes…

Read MoreAll you have to do, really, is set the service and client up so that it the service will stream the data sent to it and received from it. Then, after creating a class that extends System.IO.Stream you can watch the data from the stream get read by overriding the Read method. When the service streams the data from a stream, it uses the .Read() method to transfer the data, so by overriding this method you can determine when the service is transmitting more data.

  1. Set service and client proxy to Stream the data with TransferMode using BasicHttpBinding
  2. Create custom class that extends System.IO.Stream and override the Read method.

To set the service up to support stream it is about as simple as changing the app.config file so that TransferMode is set to “Streamed”. The article that first explained how to do this has an additional configuration of messageEncoding=”Mtom”. I don’t believe this is required, but I have used it anyways due to the fact that MSDN references indicate that it is a good encoding method (“The MTOM encoder attempts to create a balance between efficiency and interoperability” . Read more about this subject on MSDN). However, the TransferMode option is only supported by the BasicHttpBinding binding type (wsHttpBinding does not support this because the WS-RM mechanism requires processing the data as a unity to apply checksums . You can read more about this at this article).

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

Note: Notice that this is the <binding> definition for the service, not the actual <endpoint> definition for the service.

To set the client up to support streaming I chose to configure the binding programmatically using the following chunk of code:

BasicHttpBinding binding = new BasicHttpBinding(); 
EndpointAddress hostAddress = new EndpointAddress(.http://locahost:8080/MyService.); 

binding.TransferMode = TransferMode.Streamed; 
binding.MessageEncoding = WSMessageEncoding.Mtom; 

using (MyServiceClient client = new MyServiceClient(binding, hostAddress)) 
{ 

}

Just because I chose to configure my client connection programmatically doesn.t mean you have to as well. You can still use the app.config just like with the service to configure the endpoint. All you have to do is add in the binding, behavior and endpoint sections into the app.config and ensure that the endpoint name attribute matches up to the endpointConfiguration meta-tag that is automatically generated by svcutil on the proxy/client class. I only chose the programmatic solution because the URL to my service could be changed based on user input, so I couldn.t hard-code the URL into the app.config file. Most people.s service URLs won.t change.

The only remaining major piece is to create a new class that extends the Stream class and overrides the Read method. This is the class/type that you will use to send your data over the wire. In my case (and the case in the example referenced at the bottom), an event is added to the Stream derived class to trigger to the application that more data has been pulled from the service. This event is fired from the Read method, which is used (like I said earlier) by the service to retrieve the data.

public delegate void ProgressEventHandler(object sender, ProgressArgs e); 
public event ProgressEventHandler ProgressChanged; 

protected override Read(byte[] buffer, int offset, int count) 
{ 
	int bytesRead = base.Read(buffer, offset, count); 
	this.totalBytesRead += bytesRead; 

	if (this.ProgressChanged != null) 
	this.ProgressChanged(this, new ProgressArgs(totalBytesRead, this.Length)); 
}

Note: The ProressArgs class uses the totalBytesRead and length parameters to determine the percentage of completion in an additional property of the args class.

Now all you have to do is setup your custom Stream class so that it is loaded with the data that you want to send over the wire, then pass that Stream to a method of your service, and vio.la! As soon as the service starts reading from the stream it will call the overridden Read method of your custom Stream class, your overridden Read method will call the event and pass back how many bytes have been read by the service and any handlers of the ProgressChanged event will be notified how much data has been read by the service.

References

Choosing a Message Encoder:
http://msdn.microsoft.com/en-us/library/aa751889.aspx

WCF Streaming: Uploading files over HTTP:
http://kjellsj.blogspot.com/2007/02/wcf-streaming-upload-files-over-http.html

Code Project: Progress Indication while Uploading/Downloading Files using WCF:
http://www.codeproject.com/KB/WCF/WCF_FileTransfer_Progress.aspx

 

Leave a Reply

Your email address will not be published.

Humanity Verification *Captcha loading...