首页 > 解决方案 > 在缓冲模式下使用 WCF 发送大字节 [] 时内存分配失败

问题描述

我想WCF用于在客户端和服务器之间传输文件。我读了很多文章,有些人Stream为此目的使用模式。但由于它的限制,我决定使用缓冲模式创建服务。例如考虑这个:

[OperationContract]
void UploadFile(Guid systemKey, string fileName, byte[] fileContent, string userName);

服务器网络配置:

...
<system.web>
    <compilation debug="true" targetFramework="4.6" />
    <httpRuntime targetFramework="4.6" maxRequestLength="2147483647" executionTimeout="7200"/>
</system.web>
....
<bindings>
  <basicHttpBinding>
    <binding name="myBasicBinding" maxBufferSize="2147483647" 
                                   maxReceivedMessageSize="2147483647" 
                                   closeTimeout="01:50:00" 
                                   openTimeout="01:50:00" 
                                   sendTimeout="01:50:00" 
                                   receiveTimeout="01:50:00" 
                                   messageEncoding="Mtom">
      <readerQuotas maxDepth="128" maxStringContentLength="8388608" maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
    </binding>
  </basicHttpBinding>
</bindings>
....
<system.webServer>
   <security>
      <requestFiltering>
           <requestLimits maxAllowedContentLength="4294967295"/>
      </requestFiltering>
   </security>
   <modules runAllManagedModulesForAllRequests="true"/>
   <directoryBrowse enabled="true"/>
</system.webServer>

和客户端配置:

<system.web>
    <compilation debug="true" targetFramework="4.6"/>
    <httpRuntime targetFramework="4.6" maxRequestLength="2147483647" executionTimeout="7200"/>
</system.web>
...
<system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="4294967295"/>
      </requestFiltering>
    </security>
</system.webServer>
<system.serviceModel>
    <bindings>
        <basicHttpBinding>
        <binding name="basicEndpoint"
                 maxBufferSize="2147483647"
                 maxReceivedMessageSize="2147483647"
                 closeTimeout="01:50:00"
                 openTimeout="01:50:00"
                 sendTimeout="01:50:00"
                 receiveTimeout="01:50:00"
                 messageEncoding="Mtom">
          <readerQuotas maxDepth="128" maxStringContentLength="8388608" maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
       </binding>
     </basicHttpBinding>
   </bindings>
   <client>
      <endpoint address="http://localhost/fts.svc" binding="basicHttpBinding" bindingConfiguration="basicEndpoint" contract="ServiceReference1.Ifts" name="basicEndpoint"/>
   </client>
</system.serviceModel>

我可以上传400MB文件,但是当文件大小为500MB或以上时,我收到此错误:

System.InsufficientMemoryException HResult=0x8013153D 消息=未能分配 1073741824 字节的托管内存缓冲区。可用内存量可能很低。源=mscorlib

内部异常 1:OutOfMemoryException:引发了“System.OutOfMemoryException”类型的异常。\

我想我为2GB数据设置了值,但它在500MB.

谢谢

此代码在本地运行,我有16GBRAM。问题出在哪里,我应该更改什么值来上传硬文件?

谢谢

标签: c#wcfwcf-bindingc#-7.0.net-4.6

解决方案


我记得我正在处理一个大文件传输,我使用buffered模式传输但不是最好的解决方案,因为过了一段时间我遇到了一些关于内存泄漏或相关内存的异常,我将其更改为transferMode="Streamed"并且一切正常,由buffered模式意味着消息的全部内容在发送之前或接收之后都存在于内存中。虽然这对于大多数情况来说是一个很好的策略,并且对于数字签名和可靠传递等消息传递功能是必需的,但大消息可能会耗尽系统的资源。
看看这里

为了避免一直创建新缓冲区,WCF 使用BufferManager重用缓冲区,达到 指定的限制maxBufferPoolSizebuffers创建和销毁的成本很高。您可以使用BufferManager类来管理缓冲池。

您可以尝试增加maxBufferPoolSize以查看是否可以减少内存使用量。我强烈建议不要将其增加到最大值,因为我认为池中的缓冲区在应用程序域得到回收之前永远不会释放。一段时间的高流量可能会导致大量内存被使用并且永远不会被释放。

所以我建议你使用streamed模式传输大文件。

要拆分文件并使用以下链接的大型流,可能会对您有所帮助:

一个流中的多个文件,自定义流
将大文件返回为拆分 zip 文件、流或字节数组 WCF 的最佳方式

它也可以帮助你

一个流中的多个流将无法正确传递给客户端


推荐阅读