TL;DR:如何使用 WCF 流式传输已知大小的大文件,并且仍然向最终用户(网络浏览器)显示进度(Content-Length
)?
我有一个 WCF REST 服务,可以下载非常大的文件 (1-20Gb),然后将其提供给 Web 浏览器。为简化起见,将我的服务视为代理。这迫使我在绑定(bind)上设置 TransferMode = Streamed
或 TransferMode = StreamedResponse
,否则最终客户端将不得不等待源文件在实际下载开始。此外,缓冲传输模式会因大文件(RAM 使用)而终止服务器。中间磁盘存储不是一种选择。来自 TransferMode 手册页:
(...) Buffered transfers hold the entire message in a memory buffer until the transfer is complete.
但是当设置 TransferMode
为 Streamed
或 StreamedResponse
时,WCF 不再返回 header Content-length
到客户端,并添加了一个新的 header Transfer-Encoding: chunked
。这与 wikipedias article on chunked transfer 是一致的:
(...) uses the Transfer-Encoding HTTP header in place of the Content-Length header (...)
但我总是事先知道要传输的数据的大小,对于最终用户来说,不知道下载的大小是非常令人沮丧的。所以:
(如何)我可以将 WCF 绑定(bind)配置为使用“流”传输模式(更具体地说,不在发送之前缓冲整个消息)并且仍然使用 Content-Length
标题?
一些线索:
This q/a 声明 http 标准不允许在同一消息中同时使用
Transfer-Encoding
和Content-length: 123456
,所以我猜这不是一个选项?我尝试使用 IDispatchMessage.BeforeSendReply 中的检查器修改 header ,但此时
Content-Length
header 尚未删除,并且Transfer-Encoding
还没有定下来,所以“太早了”。后来我读到分块传输编码是在 TCP 级别上进行的,因此即使我可以,此时更改 header 也可能不会起作用。我尝试设置 aspNetCompatibilityEnabled
="true"
,将 wcf 传输模式设置为缓冲输出(默认),然后设置System.Web.HttpContext.Current.Response.BufferOutput =假;
。虽然 wcf 忽略了这一点,但消息显然已被缓冲。根据 this link 的说法,这似乎是一项缺失的功能。但是某处可能仍然有一个古怪的解决方法..
最佳答案
这是一个经过测试的解决方法,仅适用于 IIS 下的 WCF - 我还没有找到任何自托管服务的解决方案。
简而言之 - 打开 aspNetCompatibility这使您可以在运行时访问 System.Web.HttpContext.Current .
Web.config
:
(...)
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding transferMode="Streamed">
</binding>
</webHttpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
(...)
</system.serviceModel>
并在返回流的服务函数中:
HttpContext.Current.Response.Headers.Add("Content-Length",
contentLength.ToString());
以下任何内容都将被忽略:
<罢工>罢工>
<罢工>WebOperationContext.Current.OutgoingResponse.Headers["Content-Length"] =
contentLength.ToString();
罢工><罢工>罢工>
就这么简单!积分转到Uffe Lausen's question on Msdn
关于c# - 传输大文件 : combining streamed transfer and content-length,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22220798/