假设我有一个包含HTML代码的player.aspx页面和除了提供视频二进制文件之外什么都不做的getvideo.aspx页面,我认为以下代码在我的player.aspx页面中可以正常工作:
<div style="text-align:center"> <video controls autoplay id="video1" width="920"> <source src="http://www.mywebsite.com/getvideo.aspx?getvideo=1" type="video/mp4"> Your browser does not support HTML5 video. </video>
Response.clearheaders Response.AddHeader("Content-Type","video/mp4") Response.AddHeader("Content-Disposition","inline;filename=""newvideo.mp4""") dim Err as string = "" Dim iStream As System.IO.Stream ' Buffer to read 10K bytes in chunk: Dim buffer(buffersize) As Byte ' Length of the file: Dim length As Integer ' Total bytes to read: Dim dataToRead As Long ' Identify the file to download including its path. Dim filepath As String = "./outout/videos/newvideo.mp4" ' Identify the file name. Dim filename As String = System.IO.Path.GetFileName(filepath) ' Open the file. try iStream = New System.IO.FileStream(filepath,System.IO.FileMode.Open,IO.FileAccess.Read,IO.FileShare.Read) catch ex as exception throw new exception("Could not create FileStream for [" & filepath & "],error follows." & vbcrlf & ex.toString) end try Try ' Total bytes to read: dataToRead = iStream.Length ' Read the bytes. While dataToRead > 0 ' Verify that the client is connected. If system.web.httpcontext.current.Response.IsClientConnected Then ' Read the data in buffer length = iStream.Read(buffer,buffersize) ' Write the data to the current output stream. system.web.httpcontext.current.Response.OutputStream.Write(buffer,length) ' Flush the data to the HTML output. system.web.httpcontext.current.Response.Flush() ReDim buffer(buffersize) ' Clear the buffer dataToRead = dataToRead - length Else 'prevent infinite loop if user disconnects dataToRead = -1 End If End While Catch ex As Exception ' Trap the error,if any. err = "Error accessing " & filepath & " : " & ex.tostring Finally If IsNothing(iStream) = False Then ' Close the file. iStream.Close() End If End Try if err<>"" then throw new exception( err )
我在页面输出上得到的只是一个HTML视频播放器(chrome的基本播放器),它似乎超时并且PLAY按钮变为灰色. Chrome开发者工具中的网络工具显示它正在下载45mb并获得200响应代码.这告诉我它工作正常.虽然我收到状态为“已取消”的第二个GET请求?
如果我访问www.mywebsite.com/output/videos/myvideo.mp4然后这在浏览器中播放正常,所以我知道IIS配置为正确流式传输视频.
此外,如果我将响应内容处置更改为“附件”,则浏览器在转到我的ASPX页面时会正确强制下载视频,但这也无法在HTML播放器上正确播放.是否有一些“聪明”的HTML5 VIDEO标签正在阻止.aspx文件通过.net提供视频?或者我错过了回复标题?
谢谢!
解决方法
这里的问题可能与范围请求的HTML5视频实现有关.如果不在用于发送视频的页面中支持范围请求,它们将无法工作.
Scott Mitchell的这篇非常有用的帖子让我了解了这个案例:
http://dotnetslackers.com/articles/aspnet/Range-Specific-Requests-in-ASP-NET.aspx
还有来自Chris Coulson的另一个有用的帖子,它为C#提供了解决方案的实现!
http://blogs.visigo.com/chriscoulson/easy-handling-of-http-range-requests-in-asp-net/
我发现的代码Chris的vb.net端口在下面,为了实现它,只需将它放在你的页面中:
system.web.httpcontext.current.Response.ContentType = "video/" & convertFormat.toString RangeDownload(convertedVideo,system.web.httpcontext.current)
VB.NET代码:
private sub RangeDownload( fullpath as string,context as HttpContext) dim size as long dim start as long dim theend as long dim length as long dim fp as long =0 using reader as new StreamReader(fullpath) size = reader.BaseStream.Length start = 0 theend = size - 1 length = size '/ Now that we've gotten so far without errors we send the accept range header '* At the moment we only support single ranges. '* Multiple ranges requires some more work to ensure it works correctly '* and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 '* '* Multirange support annouces itself with: '* header('Accept-Ranges: bytes'); '* '* Multirange content must be sent with multipart/byteranges mediatype,'* (mediatype = mimetype) '* as well as a boundry header to indicate the varIoUs chunks of data. '*/ context.Response.AddHeader("Accept-Ranges","0-" + size) '// header('Accept-Ranges: bytes') '// multipart/byteranges '// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 if (not String.IsNullOrEmpty(context.Request.ServerVariables("HTTP_RANGE"))) then dim anotherStart as long = start dim anotherEnd as long = theend dim arr_split as string() = context.Request.ServerVariables("HTTP_RANGE").Split("=") 'new char[] { Convert.tochar("=") }) dim range as string = arr_split(1) '// Make sure the client hasn't sent us a multibyte range if (range.IndexOf(",") > -1) then '// (?) Shoud this be issued here,or should the first '// range be used? Or should the header be ignored and '// we output the whole content? context.Response.AddHeader("Content-Range","bytes " & start & "-" & theend & "/" & size) throw new HttpException(416,"Requested Range Not Satisfiable") end if '// If the range starts with an '-' we start from the beginning '// If not,we forward the file pointer '// And make sure to get the end byte if spesified if (range.StartsWith("-")) then '// The n-number of the last bytes is requested anotherStart = size - Convert.ToInt64(range.Substring(1)) else arr_split = range.Split("-") anotherStart = Convert.ToInt64(arr_split(0)) dim temp as long = 0 if (arr_split.Length > 1 andalso Int64.TryParse(arr_split(1).ToString(),temp)) then anotherEnd = Convert.ToInt64(arr_split(1)) else anotherEnd = size end if end if '/* Check the range and make sure it's treated according to the specs. ' * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html ' */ '// End bytes can not be larger than $end. if (anotherEnd > theend) then anotherEnd = theend else anotherEnd = anotherEnd end if '// Validate the requested range and return an error if it's not correct. if (anotherStart > anotherEnd or anotherStart > size - 1 or anotherEnd >= size) then context.Response.AddHeader("Content-Range","bytes " + start + "-" + theend + "/" + size) throw new HttpException(416,"Requested Range Not Satisfiable") end if start = anotherStart theend = anotherEnd length = theend - start + 1 '// Calculate new content length fp = reader.BaseStream.Seek(start,SeekOrigin.Begin) context.Response.StatusCode = 206 end if end using '// Notify the client the byte range we'll be outputting context.Response.AddHeader("Content-Range","bytes " & start & "-" & theend & "/" & size) context.Response.AddHeader("Content-Length",length.ToString()) '// Start buffered download context.Response.WriteFile(fullpath,fp,length) context.Response.End() end sub