WCF Stream.Read всегда возвращает 0 в клиенте
Я провел большую часть своего дня, пытаясь понять, почему это не работает. У меня есть служба WCF, которая передает объект клиенту. Затем клиент должен записать файл на свой диск. Но когда я звоню stream.Read(buffer, 0, bufferLength)
всегда возвращает 0. Вот мой код:
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;
}
}
Сервисный конфигурационный файл:
<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>
Клиент:
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); }
Клиентский 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>
(некоторый код вырезан для краткости)
Я прочитал все, что могу найти по созданию потоковых сервисов WCF, и мой код выглядит не иначе, чем их. Я могу заменить потоковую передачу буферизацией и нормально передать объект, но когда я пытаюсь выполнить потоковую передачу, клиент всегда видит поток как "пустой". testFile.exe
создается, но его размер составляет 0 КБ.
Что мне не хватает?
ОБНОВИТЬ
Хмм. Ну, я не могу отладить его, потому что при попытке сказать мне, что тестовый клиент WCF не поддерживает потоки. Но я знаю, что что-то делает это в потоке, потому что, если я не включу цикл while, а просто получу один fs.Write(...), он закроет 8KB после закрытия потока. Так что что-то пересекается.
Я посмотрел на ваш пост и добавил:
[MessageContract]
public class FileDownloadMessage
{
[MessageBodyMember(Order = 1)]
public MemoryStream FileByteStream;
}
на мой IStreamService. А также downloadStreamFile()
теперь возвращает FileDownloadMessage
объект.
Но после обновления ссылки на сервис на моем клиенте, он думает downloadStreamFile()
возвращает TestApp.StreamServiceRef.MemoryStream
объект.
Это также создало downloadStreamFileRequest()
класс, который я не сделал и понятия не имею, откуда он.
4 ответа
Прежде всего, вы можете отладить службу и посмотреть, содержит ли поток какие-либо данные на этой стороне, прежде чем он будет отправлен клиенту?
Я также возвращаю поток из моей службы WCF. У меня тоже была проблема, хотя и не совсем та. Надеюсь, решение, которое я нашел, поможет. Я нашел решение здесь. Это решение имеет дело с загрузкой потока, но реализация в моем сервисе загрузки работала нормально.
Мой SO вопрос / решение здесь (мой собственный ответ - тот, на который вы, вероятно, должны смотреть, а не принятый ответ, так как он специфичен для AJAX)
РЕДАКТИРОВАТЬ: Основные различия заключаются в том, что я обернул свой поток в MessageContract, где я указал поток в качестве единственного члена сообщения, и атрибут привязки basicHttp "TransferMode" равен "Потоковый". Я новичок в WCF, поэтому я не уверен, что это поможет, но я надеюсь, что это поможет!
РЕДАКТИРОВАТЬ 2: я бы не использовал сервисную ссылку. Вы можете запустить (из командной строки cmd) 'svcutil.exe [mex url]', чтобы получить файл конфигурации и файл.cs, которые могут быть более точными, чем то, что вы получаете из справочника службы. У меня даже нет ссылки на сервис в моем проекте, и он работает нормально. Просто добавьте этот сгенерированный файл.cs в ваш проект и добавьте для него оператор использования. Кроме того, вы можете добавить параметры конфигурации в ваше приложение или файл веб-конфигурации на клиенте. Эти 2 сгенерированных файла создаются в каталоге, в котором вы запускаете команду svcutil.
DownloadStreamFileRequest
класс - это просто объект запроса, который клиент передает base.Channel
при совершении звонка в сервис. В моем коде ниже это называется GetExportedFileRequest
В моем сгенерированном файле.cs у меня есть следующий метод, который возвращает System.IO.MemoryStream
и он получает этот поток из FileDownloadMessage
объект:
public System.IO.MemoryStream GetExportedFile()
{
GetExportedFileRequest inValue = new GetExportedFileRequest();
FileDownloadMessage retVal = ((IDailyBillingParser)(this)).GetExportedFile( inValue );
return retVal.FileByteStream;
}
У меня также была эта проблема, и что работало для меня, устанавливая позицию потока в 0, прежде чем отправить его обратно клиенту.
В случае, если кто-то ищет здесь, я по своей ошибке возвращал FileStream вместо Stream из моей службы WCF - это всегда возвращало пустой поток. Изменение типа возвращаемого значения в Stream решило мою проблему.
Есть ли какая-то причина, по которой вы хотите использовать "BinaryFormatter"? Если нет, вы можете вернуть поток из wcf, как это
вернуть новый FileStream(@"D:\yourFileName.txt", FileMode.Open, FileAccess.Read)