通过WCF传输大型二进制(byte[])文件

22

我正在尝试构建一个WCF服务,允许客户端将大型二进制文件发送到服务。

然而,我只能成功传输3-4MB的文件。(当我尝试传输4.91MB或更大的文件时,传输失败)

如果我尝试发送4.91MB的文件,则会出现以下错误:

异常消息:在接收来自http://localhost:56198/Service.svc的HTTP响应时发生错误。这可能是由于服务端点绑定未使用HTTP协议引起的。这也可能是由于服务器中止了HTTP请求上下文(可能是由于服务关闭)。请参阅服务器日志以获取更多详细信息。

内部异常消息:基础连接被关闭:接收时发生了意外错误。

内部异常消息:无法从传输连接中读取数据:现有的连接被远程主机强制关闭。

内部异常消息:现有的连接被远程主机强制关闭

当将byte []文件作为方法参数发送到公开的服务方法时,此错误会在客户端端出现。

对于成功的文件传输(小于3MB),我在服务方法的第一行设置了断点,该断点会被命中并且文件会被传输。但是,在这种情况下,一旦调用方法,就会出现错误。在此错误的情况下,服务中的断点不会受到影响。

我将粘贴我的Service Web.config和Asp Page(Client)Web.config的代码片段。如果您还需要发送文件和接受文件的代码,请告诉我,我会发送那部分代码。

Service Web.Config

<system.serviceModel>
<bindings>
  <basicHttpBinding>
    <binding name="basicHttpEndpointBinding" closeTimeout="01:01:00"
      openTimeout="01:01:00" receiveTimeout="01:10:00" sendTimeout="01:01:00"
      allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
      maxBufferSize="2147483646" maxBufferPoolSize="2147483646" maxReceivedMessageSize="2147483646"
      messageEncoding="Mtom" textEncoding="utf-8" transferMode="StreamedRequest"
      useDefaultWebProxy="true">
      <readerQuotas maxDepth="2147483646" maxStringContentLength="2147483646" maxArrayLength="2147483646"
        maxBytesPerRead="2147483646" maxNameTableCharCount="2147483646" />
      <security mode="None">
        <transport clientCredentialType="None" proxyCredentialType="None"
          realm="" />
        <message clientCredentialType="UserName" algorithmSuite="Default" />
      </security>
    </binding>        
  </basicHttpBinding>      
</bindings>
    <services>
        <service behaviorConfiguration="DragDrop.Service.ServiceBehavior" name="DragDrop.Service.Service">
            <endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicHttpEndpointBinding" contract="DragDrop.Service.IService">
                <identity>
                    <dns value="localhost"/>
                </identity>
            </endpoint>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="DragDrop.Service.ServiceBehavior">
                <serviceMetadata httpGetEnabled="true"/>
                <serviceDebug includeExceptionDetailInFaults="false"/>
      <dataContractSerializer maxItemsInObjectGraph="2147483646"/>
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>

客户端 (Asp.net 页面) Web.Config

<system.serviceModel>
<bindings>
   <basicHttpBinding>
      <binding name="BasicHttpBinding_IService" closeTimeout="00:01:00"
         openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
         allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
         maxBufferSize="2147483646" maxBufferPoolSize="2147483646" maxReceivedMessageSize="2147483646"
         messageEncoding="Mtom" textEncoding="utf-8" transferMode="StreamedResponse"
         useDefaultWebProxy="true">
         <readerQuotas maxDepth="2147483646" maxStringContentLength="2147483646" maxArrayLength="2147483646"
            maxBytesPerRead="2147483646" maxNameTableCharCount="2147483646" />
         <security mode="None">
            <transport clientCredentialType="None" proxyCredentialType="None"
               realm="">
               <extendedProtectionPolicy policyEnforcement="Never" />
            </transport>
            <message clientCredentialType="UserName" algorithmSuite="Default" />
         </security>
      </binding>
   </basicHttpBinding>
</bindings>

<behaviors>
  <endpointBehaviors>
    <behavior name="debuggingBehaviour">
      <dataContractSerializer maxItemsInObjectGraph="2147483646" />
    </behavior>
  </endpointBehaviors>
</behaviors>

<client>
   <endpoint address="http://localhost:56198/Service.svc" binding="basicHttpBinding"
      bindingConfiguration="BasicHttpBinding_IService" contract="ServiceReference.IService"
      name="BasicHttpBinding_IService" behaviorConfiguration="debuggingBehaviour" />
</client>
</system.serviceModel>

抱歉,我正在编辑(我的服务配置不知何故没有显示出来....)你还需要任何C#代码吗...??? - user402186
我已经解决了这个问题:https://dev59.com/6XNA5IYBdhLWcg3wWsUA - daniele3004
4个回答

18

虽然我同意使用流式传输会更好,但是下面的方法可以使其正常工作而无需进行其他更改。

您还需要在Web.config中增加最大消息长度:

<configuration>
  <system.web>
  <httpRuntime maxMessageLength="409600"
    executionTimeoutInSeconds="300"/>
  </system.web>
</configuration>

这将将最大消息长度设置为400 MB(参数以kB为单位)。请查看此MSDN页面获取更多信息。

谢谢...它正在工作...我刚刚测试了一个30MB的文件,它正在工作,我将进行1-2GB的测试,然后看看情况如何,但现在,我的问题已经得到了回答...非常感谢。 - user402186
我没太明白你的意思。你是指我应该分块传输文件(每个块都通过流传输,就像我们刚才为30MB的文件所做的那样)。然后当所有块都到达服务器后,再将它们拼接在一起...这样可以吗?还是有更好的方法吗? - user402186
1
你是指上面分享的链接中所解释的方式吗:http://msdn.microsoft.com/zh-cn/library/ms789010.aspx - user402186
1
不是要抢答案,但我添加了一个只显示这个的答案 :-) 基本上,你需要返回一个 Stream 而不是一个 byte[]。祝好运! - Jakob Möllås
@Jakob:没问题。我决定不刻意重述你和Nick的答案,所以添加了一个链接,至少指出了可能性 :) - Thorarin
显示剩余3条评论

18

正如指出的那样,尝试使用流传输,这里有一些示例代码,展示了使用流传输发送和接收(可能)大量数据的情况。

使用像这样的绑定,注意MaxReceivedMessageSizeTranferMode设置。

<binding name="Streaming_Binding" maxReceivedMessageSize="67108864"  
    messageEncoding="Text" textEncoding="utf-8" transferMode="Streamed">
</binding>

添加一些服务代码

[OperationContract]
public Stream GetLargeFile()
{
    return new FileStream(path, FileMode.Open, FileAccess.Read);
}

[OperationContract]
public void SendLargeFile(Stream stream)
{
    // Handle stream here - e.g. save to disk    
    ProcessTheStream(stream);

    // Close the stream when done processing it
    stream.Close();
}

还有一些客户端代码

public Stream GetLargeFile()
{
    var client = /* create proxy here */;
    try
    {
        var response = client.GetLargeFile();

        // All communication is now handled by the stream, 
        // thus we can close the proxy at this point
        client.Close();

        return response;
    }
    catch (Exception)
    {
        client.Abort();
        throw;
    }
}

public void SendLargeFile(string path)
{
    var client = /* create proxy here */;
    client.SendLargeFile(new FileStream(path, FileMode.Open, FileAccess.Read));
}

另外,请确保您没有遇到超时问题,因为传输大文件可能需要一些时间(默认的receiveTimeout为10分钟)。

您可以在此处下载Microsoft WCF/WF示例代码(这里)(撰写本文时,顶部的C#链接已损坏,但其他示例代码似乎都没问题)。


谢谢Jakob,我有点明白你的答案了,但是我不理解的是,你从服务端返回一个流给客户端。我想要的是相反的,即客户端将大文件上传到服务器......或者我理解你的回答错了吗? - user402186
啊哈,抱歉,我错过了那个,好吧,这更多或少是一个反向处理的问题。添加代码来展示它! - Jakob Möllås
1
正确!我改了名字以避免未来混淆。 - Jakob Möllås
样例链接像往常一样失效了(不是你的错)。 - Veverke
@Veverke:谢谢,已更新链接,尽管示例页面仍然有些问题。 - Jakob Möllås

5

你是否考虑过使用流式传输?

Windows Communication Foundation (WCF) 可以使用缓冲或流传输发送消息。在默认的缓冲传输模式中,必须完全传递消息后接收方才能读取它。在流传输模式中,接收方可以在消息完全传递之前开始处理消息。当传递的信息很长且可以被串行处理时,流传输模式非常有用。当消息太大无法完全缓存时,流传输模式也很有用。

http://msdn.microsoft.com/en-us/library/ms789010.aspx


我正在使用“Streamed”作为传输模式。在我的服务器Web.config中,我有:messageEncoding="Mtom" textEncoding="utf-8" transferMode="Streamed"。 - user402186
在上面的配置中,我使用了transferMode="StreamedRequest",我尝试过这种方式,但它也没有起作用。 - user402186
您还需要修改您的合约以使用流对象。 - Nick
我正在尝试添加传输模式,但在web.config文件中会出现错误。 - user3217843

2
我将重复其他人所说的话,并表示在使用Windows Communication Foundation时,使用流传输是最好的选择。下面是一份优秀的指南,它解释了所有步骤,以便通过WCF流传输文件。它非常全面且信息丰富。
这里是指南:有关在WCF上流传输文件的指南

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接