我正在通过 IIS 返回 WCF 服务中的范围请求的视频文件。
代码的结尾如下所示:
WriteResponseHeaders(stuff);
while (remainingBytes > 0)
{
if (response.IsClientConnected) // response is a System.Web.HttpResponse
{
int chunkSize = stream.Read(buffer, 0, 10240 < remainingBytes ? 10240 : remainingBytes);
response.OutputStream.Write(buffer, 0, chunkSize);
remainingBytes -= chunkSize;
response.Flush();
}
else
{
return;
}
}
在 Firefox、Internet Explorer 和 Opera 中它可以正常工作。在 Chrome 中,视频将在结束前停止播放一段时间。 Fiddler 显示 504 错误:
[Fiddler] ReadResponse() failed: The server did not return a response for this request. Server returned 16556397 bytes.
如果我在循环后面设置一个断点,并让程序坐在那里直到视频超过其停止点,Chrome 将毫无问题地播放完整视频,Fiddler 将显示带有所有正确 header 的响应等等。在该断点和调用结束之间执行的唯一代码是刷新日志流。
作为测试,我陷入了:
while (response.IsClientConnected)
{
System.Threading.Thread.Sleep(1000);
}
循环和播放在所有浏览器中都正常。我的回答在 Fiddler 中看起来也很好。当然,这有太多问题,无法成为适当的解决方案,但它似乎向我表明,这更多是时间问题,而不是行为问题。
为什么允许代码过快地超过这一点会导致问题以及如何防止它这样做?
最佳答案
尝试返回 Stream,而不是写入response.OutputStream。
[ServiceContract]
public interface IStreamingService
{
[OperationContract]
[WebGet(BodyStyle=WebMessageBodyStyle.Bare, UriTemplate = "/video?id={id}")]
Stream GetVideo(string id);
}
public class StreamingService : IStreamingService
{
public System.IO.Stream GetVideo(string id)
{
Stream stream = File.OpenRead("c:\\Temp\\Video.mp4");
//WriteResponseHeaders(stuff);
return stream;
}
}
更新:
如果您想支持查找,您可以将 block 复制到 byte[] 并返回 MemoryStream,或者您可以将流包装在仅返回完整文件的一部分的代理中。
public class PartialStream : Stream
{
private Stream underlying;
private long offset;
private long length;
public PartialStream(Stream underlying, long offset, long length)
{
this.underlying = underlying;
this.offset = offset;
if (offset + length > underlying.Length) {
this.length = underlying.Length - offset;
} else {
this.length = length;
}
this.underlying.Seek(offset, SeekOrigin.Begin);
}
public override bool CanRead { get { return true; } }
public override bool CanSeek { get { return false; } }
public override bool CanWrite { get { return false; } }
public override void Flush()
{
throw new NotSupportedException();
}
public override long Length
{
get { return this.length; }
}
public override long Position
{
get
{
return this.underlying.Position - offset;
}
set
{
this.underlying.Position = offset + Math.Min(value,this.length) ;
}
}
public override int Read(byte[] buffer, int offset, int count)
{
if (this.Position + offset >= this.length)
return 0;
if (this.Position + offset + count > this.length) {
count = (int)(this.length - this.Position - offset);
}
return underlying.Read(buffer, offset, count);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
this.underlying.Dispose();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
并且您必须尊重 Range 请求 header 。
public System.IO.Stream GetVideo(string id)
{
RangeHeaderValue rangeHeader;
bool hasRangeHeader = RangeHeaderValue.TryParse(
WebOperationContext.Current.IncomingRequest.Headers["Range"],
out rangeHeader);
var response = WebOperationContext.Current.OutgoingResponse;
Stream stream = File.OpenRead("c:\\Temp\\Video.mp4");
var offset = hasRangeHeader ? rangeHeader.Ranges.First().From.Value : 0;
response.Headers.Add("Accept-Ranges", "bytes");
response.ContentType = "video/mp4";
if (hasRangeHeader) {
response.StatusCode = System.Net.HttpStatusCode.PartialContent;
var totalLength = stream.Length;
stream = new PartialStream(stream, offset, 10 * 1024 * 1024);
var header = new ContentRangeHeaderValue(offset, offset + stream.Length - 1,totalLength);
response.Headers.Add("Content-Range", header.ToString());
}
response.ContentLength = stream.Length;
return stream;
}
关于c# - 为什么在断点处继续太快会使我的服务器发送无效响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25213260/