Я пытаюсь загрузить/загрузить файл через службу 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> ;"; border = "uuid: b230e809-500b-4217-a08e-32ff49e13bac + id = 5"; start-info = "text/xml" был отправлен службе, ожидающей text/xml; кодировка = UTF-8. Связи клиента и службы могут быть несовместимы.
Я потратил несколько дней на то, чтобы это работало и действительно не знаю, что еще я могу попробовать. Я был бы действительно очень рад, если бы кто-нибудь мог посмотреть на конфиги и, возможно, дать подсказку, что еще попробовать. Итак, вопрос:
Что может быть причиной неудачной передачи и что я могу сделать, чтобы решить эту проблему?
Я также был бы рад за дальнейшие консультации по
Что делать, чтобы найти здесь корень проблемы, если он не может быть идентифицирован на основе информации, заданной в вопросе?
Приложение: Я не думаю, что исключение на стороне клиента имеет какое-то значение, но я хотел бы включить его в вопрос, чтобы облегчить для других с той же проблемой поиск ответов (надеюсь):
В System.dll произошло первое исключение типа "System.Net.Sockets.SocketException"
Дополнительная информация: существующее соединение было принудительно закрыто удаленным хостом
Я не вижу, где привязка службы указывается в файле конфигурации serivce. Я подозреваю, что значения конфигурации не читаются, и это является причиной того, почему.
Поэтому я предлагаю указать имя привязки в файле конфигурации службы как BasicHttpBinding_ITransferService
(то же имя, что и в клиентской конфигурации), а затем добавить следующее под узлом system.serviceModel
файла конфигурации:
<services>
<service name="TransferService" >
<endpoint
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_ITransferService"
contract="Transfer.ITransferService" />
</service>
</services>
Services
подsystem.ServiceModel
?