Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/31.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 流式传输大型视频文件.net_C#_Asp.net_Video Streaming_Webforms_Ihttphandler - Fatal编程技术网

C# 流式传输大型视频文件.net

C# 流式传输大型视频文件.net,c#,asp.net,video-streaming,webforms,ihttphandler,C#,Asp.net,Video Streaming,Webforms,Ihttphandler,我试图从HttpHandler以webforms的形式传输一个大文件。它似乎不起作用,因为它没有流式传输文件。相反,它将文件读入内存,然后将其发送回客户端。我到处寻找解决方案,解决方案告诉我,当他们做同样的事情时,他们会流式处理文件。我的解决方案是这样的: using (Stream fileStream = File.OpenRead(path)) { context.Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(360.0)

我试图从HttpHandler以webforms的形式传输一个大文件。它似乎不起作用,因为它没有流式传输文件。相反,它将文件读入内存,然后将其发送回客户端。我到处寻找解决方案,解决方案告诉我,当他们做同样的事情时,他们会流式处理文件。我的解决方案是这样的:

using (Stream fileStream = File.OpenRead(path))
{
    context.Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(360.0));
    context.Response.Cache.SetCacheability(HttpCacheability.Public);
    context.Response.AppendHeader("Content-Type", "video/mp4");
    context.Response.AppendHeader("content-length", file.Length);
    byte[] buffer = new byte[1024];
    while (true)
    {
      if (context.Response.IsClientConnected)
     {
       int bytesRead = fileStream.Read(buffer, 0, buffer.Length);
       if (bytesRead == 0) break;
       context.Response.OutputStream.Write(buffer, 0, bytesRead);
       context.Response.Flush();
     }
     else
     {
       break;
     }

   }
   context.Response.End();
}

对于小文件,如果我调试代码,它将播放视频,但直到它到达context.Respond.End()行。但对于大文件,这将不起作用,因为它将整个文件存储在内存中,这将带来问题。

我也遇到过类似的问题,视频在播放之前必须完全下载

我可以看到你想流视频,更具体地说。 你必须小心编码(确保它是可流化的),不要只依赖扩展名,因为创建文件的人可以用一种更巧妙的方式构建视频,但99%的时候你应该是好的。我用。 在您的情况下,应该是H.264

它还取决于浏览器和您用于流式处理的内容(后端代码除外)。在我的例子中,我使用了Chrome/Html5和.webm(VP8/Ogg-Vorbis)。它适用于1G以上的文件。没有测试是否大于4G

我用于下载视频的代码:

    public void Video(string folder, string name) {
        string filepath = Server.MapPath(String.Format("{0}{1}", HttpUtility.UrlDecode(folder), name));
        string filename = name;

        System.IO.Stream iStream = null;
        byte[] buffer = new Byte[4096];
        int length;
        long dataToRead;

        try {
            // Open the file.
            iStream = new System.IO.FileStream(filepath, System.IO.FileMode.Open,
                        System.IO.FileAccess.Read, System.IO.FileShare.Read);


            // Total bytes to read:
            dataToRead = iStream.Length;

            Response.AddHeader("Accept-Ranges", "bytes");
            Response.ContentType = MimeType.GetMIMEType(name);

            int startbyte = 0;

            if (!String.IsNullOrEmpty(Request.Headers["Range"])) {
                string[] range = Request.Headers["Range"].Split(new char[] { '=', '-' });
                startbyte = Int32.Parse(range[1]);
                iStream.Seek(startbyte, SeekOrigin.Begin);

                Response.StatusCode = 206;
                Response.AddHeader("Content-Range", String.Format(" bytes {0}-{1}/{2}", startbyte, dataToRead - 1, dataToRead));
            }

            while (dataToRead > 0) {
                // Verify that the client is connected.
                if (Response.IsClientConnected) {
                    // Read the data in buffer.
                    length = iStream.Read(buffer, 0, buffer.Length);

                    // Write the data to the current output stream.
                    Response.OutputStream.Write(buffer, 0, buffer.Length);
                    // Flush the data to the HTML output.
                    Response.Flush();

                    buffer = new Byte[buffer.Length];
                    dataToRead = dataToRead - buffer.Length;
                } else {
                    //prevent infinite loop if user disconnects
                    dataToRead = -1;
                }
            }
        } catch (Exception ex) {
            // Trap the error, if any.
            Response.Write("Error : " + ex.Message);
        } finally {
            if (iStream != null) {
                //Close the file.
                iStream.Close();
            }
            Response.Close();
        }
    }

确保您的响应标题包含您需要的所有内容。

这里真正重要的是“范围”标题。虽然现有的答案是正确的,但没有任何解释

当您在未指定范围的情况下发出请求时,整个文件都将以流方式传输。视频播放器自动指定带有起始字节的“范围”标题,起始字节与播放器在视频中的位置一致

因为这是HTTP固有的一部分,所以在

“Accept Range:bytes”头告诉客户机我们希望接受范围头作为字节计数。状态代码“206”告诉客户端我们发送了部分内容,也就是整个文件的一部分。“Content Range:start-end/total”头告诉客户端我们在当前请求中发回的信息的范围

以下是一个功能完整的代码片段:

public static void RespondFile(this HttpListenerContext context, string path, bool download = false) {

    HttpListenerResponse response = context.Response;

    // tell the browser to specify the range in bytes
    response.AddHeader("Accept-Ranges", "bytes");

    response.ContentType = GetMimeType(path);
    response.SendChunked = false;

    // open stream to file we're sending to client
    using(FileStream fs = File.OpenRead(path)) {

        // format: bytes=[start]-[end]
        // documentation: https://tools.ietf.org/html/rfc7233#section-4
        string range = context.Request.Headers["Range"];
        long bytes_start = 0,
        bytes_end = fs.Length;
        if (range != null) {
            string[] range_info = context.Request.Headers["Range"].Split(new char[] { '=', '-' });
            bytes_start = Convert.ToInt64(range_info[1]);
            if (!string.IsNullOrEmpty(range_info[2])) 
                bytes_end = Convert.ToInt64(range_info[2]);
            response.StatusCode = 206;
            response.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", bytes_start, bytes_end - 1, fs.Length));
        }

        // determine how many bytes we'll be sending to the client in total
        response.ContentLength64 = bytes_end - bytes_start;

        // go to the starting point of the response
        fs.Seek(bytes_start, SeekOrigin.Begin);

        // setting this header tells the browser to download the file
        if (download) 
            response.AddHeader("content-disposition", "attachment; filename=" + Path.GetFileName(path));

        // stream video to client
        // note: closed connection during transfer throws exception
        byte[] buffer = new byte[HttpServer.BUFFER_SIZE];
        int bytes_read = 0;
        try {

            while (fs.Position < bytes_end) {
                bytes_read = fs.Read(buffer, 0, buffer.Length);
                response.OutputStream.Write(buffer, 0, bytes_read);
            }

            response.OutputStream.Close();

        } catch(Exception) {}

    }

}
public static void RespondFile(此HttpListenerContext上下文,字符串路径,bool download=false){
HttpListenerResponse=context.response;
//告诉浏览器以字节为单位指定范围
AddHeader(“接受范围”、“字节”);
response.ContentType=GetMimeType(路径);
response.SendChunked=false;
//打开要发送到客户端的文件流
使用(FileStream fs=File.OpenRead(path)){
//格式:字节=[start]-[end]
//文件:https://tools.ietf.org/html/rfc7233#section-4
字符串范围=context.Request.Headers[“range”];
长字节\u start=0,
字节\u end=fs.Length;
如果(范围!=null){
string[]range_info=context.Request.Headers[“range”].Split(新字符[]{'=','-});
字节\u start=Convert.ToInt64(范围\u信息[1]);
如果(!string.IsNullOrEmpty(范围信息[2]))
字节\u end=Convert.ToInt64(范围\u信息[2]);
response.StatusCode=206;
AddHeader(“Content Range”,string.Format(“bytes{0}-{1}/{2}”,bytes_start,bytes_end-1,fs.Length));
}
//确定我们将向客户端发送的总字节数
response.ContentLength64=字节\结束-字节\开始;
//转到响应的起点
Seek(字节开始,见korigin.Begin);
//设置此标题会告诉浏览器下载文件
if(下载)
AddHeader(“内容处置”、“附件;文件名=“+Path.GetFileName(Path));
//将视频流传输到客户端
//注意:传输期间关闭的连接引发异常
byte[]buffer=新字节[HttpServer.buffer_SIZE];
int bytes_read=0;
试一试{
while(fs.Position

请注意,我们可以简单地检查文件流的“位置”(以字节为单位),而不是跟踪我们总共发送了多少字节。

Maxad的答案是完美的答案。我还对.Net Core版本做了一些更改:

<video id="myvideo" height="400" width="600" controls>
    <source src="../api/StreamApi/GetStream" type="video/mp4"/>
</video>

    [Route("api/StreamApi/GetStream")]
    [HttpGet]
    public async Task GetStream()
    {
        string filepath = @"C:\temp\car.mp4";
        string filename = Path.GetFileName(filepath);

        System.IO.Stream iStream = null;
        byte[] buffer = new Byte[4096];
        int length;
        long dataToRead;

        try
        {
            // Open the file.
            iStream = new System.IO.FileStream(filepath, System.IO.FileMode.Open,
                        System.IO.FileAccess.Read, System.IO.FileShare.Read);


            // Total bytes to read:
            dataToRead = iStream.Length;

            Response.Headers["Accept-Ranges"] = "bytes";
            Response.ContentType = "application/octet-stream";

            int startbyte = 0;

            if (!String.IsNullOrEmpty(Request.Headers["Range"]))
            {
                string[] range = Request.Headers["Range"].ToString().Split(new char[] { '=', '-' });
                startbyte = Int32.Parse(range[1]);
                iStream.Seek(startbyte, SeekOrigin.Begin);

                Response.StatusCode = 206;
                Response.Headers["Content-Range"] = String.Format(" bytes {0}-{1}/{2}", startbyte, dataToRead - 1, dataToRead);
            }
            var outputStream = this.Response.Body;
            while (dataToRead > 0)
            {
                // Verify that the client is connected.
                if (HttpContext.RequestAborted.IsCancellationRequested == false)
                {
                    // Read the data in buffer.
                    length = await iStream.ReadAsync(buffer, 0, buffer.Length);

                    // Write the data to the current output stream.
                    await outputStream.WriteAsync(buffer, 0, buffer.Length);
                    // Flush the data to the HTML output.
                    outputStream.Flush();

                    buffer = new Byte[buffer.Length];
                    dataToRead = dataToRead - buffer.Length;
                }
                else
                {
                    //prevent infinite loop if user disconnects
                    dataToRead = -1;
                }
            }
        }
        catch (Exception ex)
        {
            // Trap the error, if any.
          
        }
        finally
        {
            if (iStream != null)
            {
                //Close the file.
                iStream.Close();
            }
            Response.Clear();
        }
    }

[路由(“api/StreamApi/GetStream”)]
[HttpGet]
公共异步任务GetStream()
{
字符串文件路径=@“C:\temp\car.mp4”;
字符串filename=Path.GetFileName(filepath);
System.IO.Stream iStream=null;
字节[]缓冲区=新字节[4096];
整数长度;
长数据存储;
尝试
{
//打开文件。
iStream=new System.IO.FileStream(文件路径,System.IO.FileMode.Open,
System.IO.FileAccess.Read、System.IO.FileShare.Read);
//要读取的总字节数:
dataToRead=iStream.Length;
响应.头文件[“接受范围”]=“字节”;
Response.ContentType=“应用程序/八位字节流”;
int startbyte=0;
如果(!String.IsNullOrEmpty(Request.Headers[“Range”]))
{
string[]range=Request.Headers[“range”].ToString().Split(新字符[]{'=','-});
startbyte=Int32.Parse(范围[1]);
iStream.Seek(startbyte,SeekOrigin.Begin);
Response.StatusCode=206;
Response.Headers[“Content Range”]=String.Format(“字节{0}-{1}/{2}”,startbyte,dataToRead-1,dataToRead);
}
var outputStream=this.Response.Body;
而(数据读取>0)
{
//验证客户端是否已连接。
如果(HttpContext.Reques