I've spent most of my day trying to figure out why this isn't working. I have a WCF service that streams an object to the client. The client is then supposed to write the file to its disk. But when I call stream.Read(buffer, 0, bufferLength)
it always returns 0. Here's my code:
namespace StreamServiceNS
{
[ServiceContract]
public interface IStreamService
{
[OperationContract]
Stream downloadStreamFile();
}
}
class StreamService : IStreamService
{
public Stream downloadStreamFile()
{
ISSSteamFile sFile = getStreamFile();
BinaryFormatter bf = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
bf.Serialize(stream, sFile);
return stream;
}
}
Service config file:
<system.serviceModel>
<services>
<service name="StreamServiceNS.StreamService">
<endpoint address="stream" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IStreamService"
name="BasicHttpEndpoint_IStreamService" contract="SWUpdaterService.ISWUService" />
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IStreamService" transferMode="StreamedResponse"
maxReceivedMessageSize="209715200"></binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceThrottling maxConcurrentCalls ="100" maxConcurrentSessions="400"/>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Client:
TestApp.StreamServiceRef.StreamServiceClient client = new StreamServiceRef.StreamServiceClient();
try
{
Stream stream = client.downloadStreamFile();
int bufferLength = 8 * 1024;
byte[] buffer = new byte[bufferLength];
FileStream fs = new FileStream(@"C:\test\testFile.exe", FileMode.Create, FileAccess.Write);
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, bufferLength)) > 0)
{
fs.Write(buffer, 0, bytesRead);
}
stream.Close();
fs.Close();
}
catch (Exception e) { Console.WriteLine("Error: " + e.Message); }
Client app.config:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpEndpoint_IStreamService" maxReceivedMessageSize="209715200" transferMode="StreamedResponse">
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://[server]/StreamServices/streamservice.svc/stream"
binding="basicHttpBinding" bindingConfiguration="BasicHttpEndpoint_IStreamService"
contract="StreamServiceRef.IStreamService" name="BasicHttpEndpoint_IStreamService" />
</client>
</system.serviceModel>
(some code clipped for brevity)
I've read everything I can find on making WCF streaming services, and my code looks no different than theirs. I can replace the streaming with buffering and send an object that way fine, but when I try to stream, the client always sees the stream as "empty". The testFile.exe
gets created, but its size is 0KB.
What am I missing?
UPDATE
Hrm. Well, I can't debug it because when I try to it tells me that the WCF Test Client does not support Streams. But, I know something is making it across in the stream because if I don't put the while loop, and just have one fs.Write(...), it will write 8KB once the stream closes. So something is getting across.
I looked at your post and added:
[MessageContract]
public class FileDownloadMessage
{
[MessageBodyMember(Order = 1)]
public MemoryStream FileByteStream;
}
to my IStreamService. And downloadStreamFile()
now returns a FileDownloadMessage
object.
But after updating the service reference on my client, it thinks downloadStreamFile()
returns a TestApp.StreamServiceRef.MemoryStream
object.
It also created a downloadStreamFileRequest()
class, which I did not make and have no idea where it's coming from.
First of all, are you able to debug the service and see if the stream contains any data on that side before it gets sent to the client?
I am returning a stream from my WCF service as well. I had a problem with it, too, though not quite the same issue. Hopefully the solution that I found will help. I found the solution here. This solution is dealing with uploading a stream, but the implementation in my download service worked fine.
My SO question/solution is here (My own answer is the one you should probably look at, not the accepted answer as that is specific to AJAX)
EDIT: The basic differences is that I wrapped my Stream in a MessageContract where I specified the Stream as the only member of the message and the basicHttp binding 'transferMode' attribute is 'Streamed'. I'm new to WCF, so I can't be sure this will help, but I hope it does!
EDIT 2: I wouldn't use a service reference. You can run (from cmd prompt) 'svcutil.exe [mex url]' to get a config file and a .cs file that may be more accurate than what you get from the service reference. I don't even have a service reference in my project and it works fine. Just add that generated .cs file to your project and add a 'using' statement for that. Also, you can add the config settings to your app or web config file on the client. Those 2 generated files get created in the directory you run the svcutil command in.
The DownloadStreamFileRequest
class is just a request object that the client passes to the base.Channel
when making the call to the service. In my code below, it's called GetExportedFileRequest
In my generated .cs file, I have the following method that returns System.IO.MemoryStream
and it gets this stream from the FileDownloadMessage
object:
public System.IO.MemoryStream GetExportedFile()
{
GetExportedFileRequest inValue = new GetExportedFileRequest();
FileDownloadMessage retVal = ((IDailyBillingParser)(this)).GetExportedFile( inValue );
return retVal.FileByteStream;
}