我正在通过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()失败:服务器未返回此请求的响应。服务器返回16556397字节。
如果我在循环之后粘贴断点,并让程序坐在那里直到视频已经超过其停止点,Chrome将播放完整的视频而没有任何问题,Fiddler将显示所有正确标题的响应等等。在该断点和调用结束之间执行的唯一代码是刷新日志流。
作为测试,我坚持:
while (response.IsClientConnected)
{
System.Threading.Thread.Sleep(1000);
}
循环播放后,所有浏览器播放都很好。我对Fiddler的反应也很好。当然,这有太多问题需要成为一个合适的解决方案,但它似乎告诉我,这是一个更多的时间问题而不是行为问题。
为什么允许代码过早地超过这一点会导致问题,如何阻止它这样做呢?
答案 0 :(得分:2)
尝试返回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;
}
}
更新
如果你想支持搜索,你可以将块复制到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请求标题。
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;
}