WCF Upload pliku

0

Witam ! Potrzebuję napisać usługę w wcf, która przyjmie od zewnętrznej aplikacji plik i zapisze go u siebie na dysku. Inne metody działają okej, ale gdy chcę przesłać plik do wcf otrzymuję : Serwer zdalny zwrócił nieoczekiwaną odpowiedź: (400) Bad Request. i niestety mimo moich kombinacji nie mogę sobie z tym problemem poradzić :/

Ogólnie, to mam metodę, która prezentuje się następująco

 
public void AddPhoto(uint userID, UploadFile file)
        {
            #region fileUpload
            string guid = Guid.NewGuid().ToString().Replace("-", "");
            string newFileName = guid + file.GetExtention();

            if (file.GetExtention() == ".jpg" || file.GetExtention() == ".jpeg" || file.GetExtention() == ".png" || file.GetExtention() == ".gif")
            {
                if (!File.Exists(@"E:\uploads\"+ newFileName))
                {
                    using (FileStream fs = new FileStream(@"E:\uploads\" + newFileName, FileMode.Create))
                    {
                        fs.Write(file.File, 0, file.File.Length);
                        fs.Close();
                    }
                }
            }

            #endregion

            PhotoData photoData = new PhotoData
            {
                Title = file.Title,
                Description = file.Description,
                userID = userID,
                Path = @"E:\uploads\" + newFileName
            };

            entity.AddPhoto(photoData);
        }

klasa upload file prezentuje się następująco :

 
    [DataContract]
    public class UploadFile
    {
        [DataMember]
        public string FileName { get; set; }

        [DataMember]
        public string Title { get; set; }

        [DataMember]
        public string Description { get; set; }

        [DataMember]
        public byte[] File { get; set; }

        public string GetExtention()
        {
                if (!string.IsNullOrEmpty(FileName))
                {
                    return Path.GetExtension(FileName);
                }
                else return null;
        }

        public string GetFileNameWithoutExtention()
        {
                if (!string.IsNullOrEmpty(FileName))
                {
                    return Path.GetFileNameWithoutExtension(FileName);
                }
                else return null;
        }
    }

Zaś w taki sposób przesyłam dane od klienta na serwer :

 
            GalleryClient proxy = new GalleryClient();

            UploadFile uploadFile = new UploadFile();

            using (FileStream fs = new FileStream(@"C:\stone.jpg", FileMode.Open, FileAccess.Read))
            {
                byte[] buffor = new byte[fs.Length];
                fs.Read(buffor, 0, (int)fs.Length);
                fs.Close();

                uploadFile.File = buffor;
            }
           // parę linijek nieważnych, przypisanie stringów itd
           proxy.AddPhoto(1, uploadFile);

Do tego etapu wszystko jest ok, w obiekcie uploadFile -> tablicy byte[] File znajduje się 194233 elementów. I w tym momencie pojawia się exception który wskazuje na proxy.AddPhoto(), myślę spoko ;) Każdy normalny człowiek pomyśli, że po prostu długość przesyłanej wiadomości jest zbyt długi, ja też na to wpadłem ,poszperałem trochę i zmieniłem oby dwa pliki konfiguracyjne, teraz wyglądają tak :


config serwera :

 
<?xml version="1.0"?>
<configuration>
  
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing"
              propagateActivity="true">
        <listeners>
          <add name="traceListener"
              type="System.Diagnostics.XmlWriterTraceListener"
              initializeData= "e:\Traces.svclog" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
  
  <connectionStrings>
    <add name="MySQL" connectionString="Server=localhost;Database=gallery;Uid=root;Pwd=admin1;" />
  </connectionStrings>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  
  <system.serviceModel>
    <services>
      <service name="WCFGallery.GalleryService">
        <endpoint address="" binding="basicHttpBinding" contract="WCFGallery.IGallery" />
      </service>
    </services>

    <bindings>
      <basicHttpBinding>
        <binding name="default" maxReceivedMessageSize="40000000">
          <readerQuotas maxStringContentLength="5000000" maxArrayLength="5000000" />
        </binding>
      </basicHttpBinding>
    </bindings>
    
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>

          <dataContractSerializer maxItemsInObjectGraph="500000"/>
          
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    
    
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

oraz config aplikacji klienckiej

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <client>
      <endpoint address="http://localhost:57609/GalleryService.svc"
        binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IGallery"
        contract="ServiceReference1.IGallery" name="BasicHttpBinding_IGallery" behaviorConfiguration="bigMessage"/>
    </client>

    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IGallery" closeTimeout="00:05:00"
          openTimeout="00:05:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
          allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
          maxBufferSize="40000000" maxBufferPoolSize="524288" maxReceivedMessageSize="40000000"
          messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
          useDefaultWebProxy="true">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <security mode="None">
            <transport clientCredentialType="None" proxyCredentialType="None"
              realm="" />
            <message clientCredentialType="UserName" algorithmSuite="Default" />
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>

    <behaviors>
      <endpointBehaviors>
        <behavior name="bigMessage">
          <dataContractSerializer maxItemsInObjectGraph="500000"/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Jako, że to moje pierwsze spotkanie z WCF, nie wiem czy pliki konfiguracyjne są dobrze zbudowane, ale po prostu configi są przeklejone z wyników google. Niestety błąd cały czas występował, po długiej zabawie nie mogąc znaleźć błędu, przebłysk - włącz traceowanie aplikacji. Otrzymałem kod błędu który przedstawia się następująco :

<ExceptionString>System.ServiceModel.ProtocolException: The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.</ExceptionString>

Jednakże zarówno w aplikacji klienckiej jak i aplikacji serwerowej mam umieszczony wpis odnośnie MaxReceivedMessageSize

0

hmmm pamiętam że z tym ustawieniem było chyba coś dziwnego, musiałbym poszperać w jakiś swoich kodach
ale jest jeszcze jedna możliwość, przez WCF można przepychać strumień i działa to poprawnie, otwierasz strumien u klienta przesyłasz go, a serwer czyta strumień i już, chyba nawet zostaje automatycznie zamknięty, szczegółów jak to zrobić poszukaj w msdn lub google, bo chyba coś trzeba było w config'ach zmienić

0

Owszem, transferMode trzeba ustawić na Streamed. Więcej na msdn i codeproject. Generalnie obsługa tego niewiele się różni od strumieni lokalnych.

0

Nie tylko transferMode na streamed, kontrakt musi być odpowiedni, jako paramter otrzymujemy stream, zwracamy stream. I założę się że zadziała to tylko .Net to .Net ...
Jest to o tyle fajne że oszczędza nam trochę pamięci ponieważ nie buforuje przesyłanych danych aby zrobić z nich potem obiekt.

0

No nie tylko. Napisałem że szczegóły na msdn. Wada jest taka że nie można wznowić przerwanego transferu (gdy np zniknie łączność)

0

Mimo moich licznych prób, zmiany konfiguracji, niestety dalej mam problem, z tą wydawałoby się prostą czynnością. Posłuchałem Waszych rad, i zmieniłem program, wyjątek dostaje inny, ale tym razem niewiele on mówi. Przybliżę Wam kod : (mimo mylnej nazwy namespace, który zawiera m.in. tcp, najpierw tą wiadomość próbuję wysłać prostym basicHttpBinding -- widać po configach.

SERVER :

     [ServiceContract]
    public interface IFile
    {
        [OperationContract]
        bool UploadStream(Stream stream);
    }
    public class FileService : IFile
    {
        public bool UploadStream(Stream stream)
        {
            if (stream.Length == 0) return false;

            using (FileStream fs = File.Create(@"E:\test.jpg", (int)stream.Length))
            {
                byte[] bytesInStream = new byte[stream.Length];
                stream.Read(bytesInStream, 0, bytesInStream.Length);

                fs.Write(bytesInStream, 0, bytesInStream.Length);
            }

            return true;
        }
    }

oraz web.config

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

  <system.serviceModel> 
    <services>
      <service name="TcpStream.FileService" behaviorConfiguration="TcpStream.FileServiceBehavior">
        <endpoint address="" bindingConfiguration="HttpStreaming" binding="basicHttpBinding" contract="TcpStream.IFile">
        </endpoint>
      </service>
    </services>

    <bindings>
      <basicHttpBinding>
        <binding name="HttpStreaming" maxReceivedMessageSize="67108864" transferMode="Streamed">
          <readerQuotas maxStringContentLength="5000000" maxArrayLength="5000000" />
        </binding>
      </basicHttpBinding>
    </bindings>
    
    <behaviors>
      <serviceBehaviors>
        <behavior name="TcpStream.FileServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
           <dataContractSerializer maxItemsInObjectGraph="500000"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
  
</configuration>

**APLIKACJA KLIENCKA **

 
        static void Main(string[] args)
        {
            FileClient proxy = new FileClient();

            Stream file = File.Open(@"C:\stone.jpg", FileMode.Open, FileAccess.Read);

            proxy.UploadStream(file);

            Console.ReadLine();
        }

app.config

<configuration>
    <system.serviceModel>
      
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IFile" closeTimeout="00:05:00"
                    openTimeout="00:05:00" receiveTimeout="00:10:00" sendTimeout="00:05:00"
                    allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="67108864" maxBufferPoolSize="67108864" maxReceivedMessageSize="67108864"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Streamed"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="67108864" maxArrayLength="67108864"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
      
        <client>
            <endpoint address="http://localhost:64158/FileService.svc" binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_IFile" contract="FileService.IFile"
                name="BasicHttpBinding_IFile" behaviorConfiguration="bigMessage" />
        </client>

      <behaviors>
        <endpointBehaviors>
          <behavior name="bigMessage">
            <dataContractSerializer maxItemsInObjectGraph="500000"/>
          </behavior>
        </endpointBehaviors>
      </behaviors>
    </system.serviceModel>
</configuration>

i nasz piękny Error :

<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Error">
<TraceIdentifier>http://msdn.microsoft.com/pl-PL/library/System.ServiceModel.Diagnostics.ThrowingException.aspx</TraceIdentifier>
<Description>Zgłaszanie wyjątku.</Description>
<AppDomain>ab380b81-1-129407117446699670</AppDomain>
<Exception>
<ExceptionType>System.ServiceModel.ProtocolException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>Wystąpił problem z plikiem XML odebranym z sieci. Aby uzyskać szczegółowe informacje, zobacz wyjątek wewnętrzny.</Message>
<StackTrace>
w System.ServiceModel.Channels.HttpRequestContext.CreateMessage()
w System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, ItemDequeuedCallback callback)
w System.ServiceModel.Activation.HostedHttpTransportManager.HttpContextReceived(HostedHttpRequestAsyncResult result)
w System.ServiceModel.Activation.HostedHttpRequestAsyncResult.HandleRequest()
w System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest()
w System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequest(Object state)
w System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2()
w System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke()
w System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks()
w System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(Object state)
w System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
w System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
w System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
</StackTrace>
<ExceptionString>System.ServiceModel.ProtocolException: Wystąpił problem z plikiem XML odebranym z sieci. Aby uzyskać szczegółowe informacje, zobacz wyjątek wewnętrzny. ---&gt; System.Xml.XmlException: Nie można odczytać treści wiadomości, ponieważ jest pusta.
   --- Koniec śladu stosu wyjątków wewnętrznych ---</ExceptionString>
<InnerException>
<ExceptionType>System.Xml.XmlException, System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>Nie można odczytać treści wiadomości, ponieważ jest pusta.</Message>
<StackTrace>
w System.ServiceModel.Channels.HttpRequestContext.CreateMessage()
w System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, ItemDequeuedCallback callback)
w System.ServiceModel.Activation.HostedHttpTransportManager.HttpContextReceived(HostedHttpRequestAsyncResult result)
w System.ServiceModel.Activation.HostedHttpRequestAsyncResult.HandleRequest()
w System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest()
w System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequest(Object state)
w System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2()
w System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke()
w System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks()
w System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(Object state)
w System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
w System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
w System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
</StackTrace>
<ExceptionString>System.Xml.XmlException: Nie można odczytać treści wiadomości, ponieważ jest pusta.</ExceptionString>
</InnerException>
</Exception>
</TraceRecord>

0

Nie chce mi sie wnikac co u ciebie jest zle, ale latwo znalezc mase dzialajacych przykladów:
http://www.c-sharpcorner.com/UploadFile/afenster/5196/Default.aspx
http://www.codeproject.com/KB/WCF/WCFDownloadUploadService.aspx
http://www.codeproject.com/KB/WCF/stream-operation-in-wcf.aspx

Najpierw to poczytaj, skompiluj u siebie a gdy zrozumiesz jak działa dopiero zastosuj w twojej aplikacji.

P.S.
Radzę ci odinstalować polską nakładkę na .NET. Gdyby nie ona, mógłbyś wkleić w google treść wyjątków i wiele się dowiedzieć. Mając to po polsku zawężasz wyniki takiego wyszukiwania prawie do zera.

1 użytkowników online, w tym zalogowanych: 0, gości: 1