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)

Другие вопросы по тегам