tags:

views:

603

answers:

1

Hi.

I have successfully designed a large file upload method using WCF. Now, I would like to report progress for each unique file being loaded. In my upload method, i have the following code block:

while ((bytesRead = request.FileByteStream.Read(buffer, 0, bufferSize)) > 0)
{
    outfile.Write(buffer, 0, bytesRead);
    totalBytesRead += bytesRead; 

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

}

Which uses the deleget ProgressEventHandler as declared below:

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

I do not use delegates all that much (but am trying to learn) and got this far by following examples online. The class ProgressArgs was missing from the example but I am guessing that is where the calculation takes place and is returned? Something like: return Convert.ToInt32((totalBytesRead * 100) / fileSize) ?

So, my questions are thus:

1) Assming I have declared and called my ProgressChanged event correctly, what do I do with ProgressArgs?

2) How do I report progress back to the client? My WCF method call currently has a 'void' return type:

upload.UploadFile(fileinfo, m_objFile.InputStream);

Will I need to fire a simultaneous JavaScript method that calls a WCF JSON method or something of that nature? A detailed explanation of how to view progress from the client would be seriously appreciated.

Thanks!

PS - I am using ASP.NET 2.0 / Framework 3.5 / C# / and I am self-hosting currently.

+1  A: 

Wow, you're asking about a lot of different layers in one question.

Callbacks, not events

You're trying to do something that is .NET specific and does not map to web service programming directly. You cannot use events/delegates in web services. Instead you must define what is called a "callback contract". This is another interface that has methods on it that represent the notifications you want to tell the caller about and is applied to your service contract like so:

public interface MyCallbackContract
{
    void ProgressChanged(float percentComplete);
}

[ServiceContract(CallbackContract = typeof(MyCallbackContract)]
public interface MyServiceContract
{
    void UploadFile(Stream stream);
}

Now, callback contracts require two way (duplex) communication, so if you're just using basicHttpBinding now, you would need to switch to use wsDualHttpBinding or some non-http binding altogether which blows interop for this service out of the water. Assuming your service is entirely intranet, then this shouldn't be a showstopper.

There's a lot more to learn about the callback programming model which I won't go into here. Instead I will refer you to an awesome MSDN article by Juval Lowey which covers the subject in great detail.

How's this help me in the browser?

The second part of your question is how can you get this information from the browser and, well, first we have to accept that there's definitely no way it's communicating with the afforementioned service directly. Instead you will need to build an AJAX "bridge" service. Since the browser is pure HTTP here, you have to do file uploads using a POST to a REST based WCF service. If you wanted to simultaneously retrieve status about that upload you would then have to make polling requests to get status.

Honestly this is one of those places where the browser still just sucks as a platform. The browser knows how much data it has sent, you should be able to report progress just based on that, but it's not exposed anywhere. This is why people turn to using a Flash based upload utility because Flash does expose these details and so you don't need to poll the server to get progress.

Drew Marsh
Thanks Again Drew. I am working my way through the link you have provided. I have two follow-up comments/questions. You wrote: "Assuming your service is entirely intranet, this shouldn't be a showstopper". My service is meant to be accessible to Internet traffic and not IntRAnet users. Is this what you mean or do I misunderstand here?
Code Sherpa
My second question relates to your comment: "you have to do file uploads using a POST to a REST based WCF service". My thought was to fire a JavaScript method that polls a REST-based WCF service for progress but the upload happens by accessing the HttpPostedFile collection on form submit and sending files to the SOAP UploadFile method. Are you saying that the polling AND the upload should be handled using AJAX? Thanks again.
Code Sherpa
As far as inter vs. intranet, when you use callback contracts you're pretty much marrying yourself into a WCF only world. The concept is not a web service standard. As far as the uploading from the browser goes, If you can't use Flash and are going to do a ASP.NET HttpPostedFile upload you have another problem: that is a blocking operation. Your Page won't start to execute until the files have completed uploading. Instead you must write a custom upload handler that receives the file and then you need to have some context (an id, etc) to be able to poll for status of that upload.
Drew Marsh
So if the whole point of this service is just to support the browser, then just write a WCF rest service which you POST the file to and poll for status.
Drew Marsh
Code Sherpa