Не удается загрузить файлы размером более 50 КБ через службу WCF

1

Я пытаюсь загрузить/загрузить файл через службу WCF. Протокол передачи - HTTP. Я установил привязку к пользовательскому Streaming качестве режима передачи и пробовал в течение нескольких дней, чтобы заставить его работать исправно, без успеха. Мне удалось заставить его работать для небольших файлов. При загрузке больших файлов файл создается на сервере, записывается несколько байтов, а затем транзакция терпит неудачу.

Служба размещается в среде Windows Azure WebRole, масштабируемой до такой степени, которая, безусловно, должна быть достаточной для задачи.

При проверке журнала трассировки с (e2e файлом) с SvcTraceViewer.exe проблема выглядит так:

При чтении потока было выбрано исключение.

с стеком вызовов:

System.ServiceModel.Dispatcher.StreamFormatter.MessageBodyStream.Read(Byte[] buffer, Int32 offset, Int32 count)
DataService.TransferService.UploadFile(RemoteFileInfo request)
SyncInvokeUploadImage(Object , Object[] , Object[] )
System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

Сервисный контракт выглядит следующим образом:

[ServiceContract]
public interface ITransferService
{
    [OperationContract]
    RemoteFileInfo DownloadImage(DownloadRequest request);

    [OperationContract(IsOneWay = true)]
    void UploadImage(RemoteFileInfo request);
}

[MessageContract]
public class DownloadRequest
{
    [MessageBodyMember]
    public string FileName;
}

[MessageContract]
public class RemoteFileInfo : IDisposable
{
    [MessageHeader(MustUnderstand = true)] 
    public string FileName;

    [MessageHeader(MustUnderstand = true)] 
    public long Length;

    [MessageBodyMember(Order = 1)] 
    public System.IO.Stream FileByteStream;

    public void Dispose()
    {
        if (FileByteStream != null)
        {
            FileByteStream.Close();
            FileByteStream = null;
        }
    }
}

Вот реализация сервиса:

public class TransferService : ITransferService
{
    public RemoteFileInfo DownloadImage(DownloadRequest request)
    {
        return DownloadFile(request);            
    }

    public void UploadImage(RemoteFileInfo request)
    {            
        UploadFile(request);
    }

    public RemoteFileInfo DownloadFile(DownloadRequest request)
    {
        var result = new RemoteFileInfo();
        try
        {
            var filePath = Path.Combine(RoleEnvironment.GetLocalResource("TempStorage").RootPath, request.FileName);
            var fileInfo = new FileInfo(filePath);

            if (!fileInfo.Exists)
                throw new FileNotFoundException("File not found", request.FileName);

            var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);

            result.FileName = request.FileName;
            result.Length = fileInfo.Length;
            result.FileByteStream = stream;
        }
        catch (Exception ex)
        {
        }
        return result;
    }

    public void UploadFile(RemoteFileInfo request)
    {
        FileStream targetStream;
        var sourceStream = request.FileByteStream;

        var filePath = Path.Combine(RoleEnvironment.GetLocalResource("TempStorage").RootPath, request.FileName);

        using (targetStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
        {
            const int bufferLen = 65000;
            var buffer = new byte[bufferLen];
            var count = 0;
            while ((count = sourceStream.Read(buffer, 0, bufferLen)) > 0)
            {
                targetStream.Write(buffer, 0, count);
            }
            targetStream.Close();
            sourceStream.Close();
        }
    }
}

Я установил web.config службы WCF таким образом, чтобы включить потоковое вещание, увеличить ограничения по размеру (или, по крайней мере, те, что я знаю), и таймауты:

<?xml version="1.0"?>
<configuration>
  <system.diagnostics>
    <trace autoflush="true">
        <listeners>
          <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=2.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
            name="AzureDiagnostics">
            <filter type="" />
          </add>
        </listeners>
    </trace>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing"
              propagateActivity="true">
        <listeners>
          <add name="sdt"
              type="System.Diagnostics.XmlWriterTraceListener"
              initializeData= "log.e2e" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
  <system.web>
    <customErrors mode="Off" />
    <compilation debug="true" targetFramework="4.0" />
    <httpRuntime maxRequestLength="2097151" useFullyQualifiedRedirectUrl="true" executionTimeout="14400"   />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />

    <bindings>
      <basicHttpBinding>
        <binding closeTimeout="00:01:00" 
                 openTimeout="00:01:00" 
                 receiveTimeout="00:10:00"
                 sendTimeout="00:10:00" 
                 maxReceivedMessageSize="2147483647"
                 maxBufferSize="2147483647" 
                 transferMode="Streamed" >
          <readerQuotas maxDepth="2147483647" 
                        maxStringContentLength="2147483647"
                        maxArrayLength="2147483647"
                        maxBytesPerRead="2147483647"
                        maxNameTableCharCount="2147483647"/>
        </binding>
      </basicHttpBinding>
    </bindings>

  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>

Клиентская сторона - приложение WPF. Вызывающий код:

private void UploadImage(byte[] data, Guid guid)
{
    using (var transferService = new TransferServiceClient())
    using (var stream = new MemoryStream(data))
    {
        var uploadRequestInfo = new RemoteFileInfo();
        uploadRequestInfo.FileName = guid.ToString();
        uploadRequestInfo.Length = data.Length;
        uploadRequestInfo.FileByteStream = stream;
        transferService.UploadImage(uploadRequestInfo);
    }
}

private byte[] DownloadImage(Guid guid)
{
    using (var transferService = new TransferServiceClient())
    {
        try
        {
            var request = new DownloadRequest(guid.ToString());
            var iconFile = transferService.DownloadImage(request);
            var data = ByteArrayOperations.FromStream(iconFile.FileByteStream);
            return data;
        }
        catch (Exception)
        {
            return null;
        }
    }
}

и - наконец - клиентская сторона app.config:

<?xml version="1.0"?>
<configuration>

  <configSections>      

  <system.web>
    <httpRuntime maxRequestLength="2097150"/>
  </system.web>

  <system.serviceModel>
    <bindings>
      <basicHttpBinding>           
        <binding name="BasicHttpBinding_ITransferService" 
                 closeTimeout="04:01:00"
                 openTimeout="04:01:00" 
                 receiveTimeout="04:10:00" 
                 sendTimeout="04:01:00"
                 allowCookies="false" 
                 bypassProxyOnLocal="false"
                 hostNameComparisonMode="StrongWildcard"
                 maxBufferSize="2147483647" 
                 maxBufferPoolSize="2147483647"
                 maxReceivedMessageSize="2147483647"
                 messageEncoding="Text" 
                 textEncoding="utf-8"
                 transferMode="Streamed"
                 useDefaultWebProxy="true">
          <readerQuotas maxDepth="128"
                        maxStringContentLength="2147483647" 
                        maxArrayLength="2147483647"
                        maxBytesPerRead="2147483647" 
                        maxNameTableCharCount="2147483647" />
          <security mode="None">
            <transport clientCredentialType="None"
                    proxyCredentialType="None" realm="" />
            <message clientCredentialType="UserName" algorithmSuite="Default" />
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>

      <endpoint address="xxx/TransferService.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ITransferService"
                contract="Transfer.ITransferService" name="BasicHttpBinding_ITransferService" />
    </client>
  </system.serviceModel>
</configuration>

Обратите внимание, что из-за осквернения я добавил этот раздел:

<system.web>
  <httpRuntime maxRequestLength="2097150"/>
</system.web>

без какого-либо эффекта.

В журнале ошибок появляется еще одно исключение, которое запускается сразу после запуска службы. Я не знаю, что это значит, и не связано ли это с проблемой:

System.ServiceModel.ProtocolException

Тип содержимого multipart/related; type = "application/xop + xml"; start = "< http://tempuri.org/0&gt ;"; border = "uuid: b230e809-500b-4217-a08e-32ff49e13bac + id = 5"; start-info = "text/xml" был отправлен службе, ожидающей text/xml; кодировка = UTF-8. Связи клиента и службы могут быть несовместимы.

Я потратил несколько дней на то, чтобы это работало и действительно не знаю, что еще я могу попробовать. Я был бы действительно очень рад, если бы кто-нибудь мог посмотреть на конфиги и, возможно, дать подсказку, что еще попробовать. Итак, вопрос:

Что может быть причиной неудачной передачи и что я могу сделать, чтобы решить эту проблему?

Я также был бы рад за дальнейшие консультации по

Что делать, чтобы найти здесь корень проблемы, если он не может быть идентифицирован на основе информации, заданной в вопросе?

Приложение: Я не думаю, что исключение на стороне клиента имеет какое-то значение, но я хотел бы включить его в вопрос, чтобы облегчить для других с той же проблемой поиск ответов (надеюсь):

В System.dll произошло первое исключение типа "System.Net.Sockets.SocketException"

Дополнительная информация: существующее соединение было принудительно закрыто удаленным хостом

  • 0
    Я озадачен вашим файлом конфигурации службы. Разве не должен быть узел Services под system.ServiceModel ?
  • 0
    Привет, Эндрю, большое спасибо за внимание к этому огромному вопросу. Я не очень знаком с этой вещью WCF, но я думаю, что конфигурация не нуждается в этом, потому что служба размещена в Azure WebRole, и роль позаботится об этом. Я могу быть неправ, хотя. Я что-то упустил, хотя это размещено в WebRole?
Показать ещё 2 комментария
Теги:
azure
wpf
wcf

1 ответ

1

Я не вижу, где привязка службы указывается в файле конфигурации serivce. Я подозреваю, что значения конфигурации не читаются, и это является причиной того, почему.

Поэтому я предлагаю указать имя привязки в файле конфигурации службы как BasicHttpBinding_ITransferService (то же имя, что и в клиентской конфигурации), а затем добавить следующее под узлом system.serviceModel файла конфигурации:

<services>
  <service name="TransferService" >
    <endpoint 
        binding="basicHttpBinding"
        bindingConfiguration="BasicHttpBinding_ITransferService"
        contract="Transfer.ITransferService" />
  </service>
</services>

Ещё вопросы

Сообщество Overcoder
Наверх
Меню