如何在html文件夹之外流式传输视频,没有错误?

时间:2017-01-10 15:20:35

标签: php html5 ubuntu-16.04

在ubuntu 16上这是在/ html文件夹外的var / www / uploads,现在是chmod 777(测试)。如果您在下载视频时尝试暂停视频,则会播放该错误:

image.php

<?php

    $filename = $_GET['filename'];
    header('Content-Type: video/mp4');
    readfile("../uploads/" . $filename);
?>

html

<video id="my_video_1" class="video-js vjs-default-skin" width="100%" height="100%"
      controls preload="none" poster='img.png'
      data-setup='{ "playbackRates": [1, 1.5, 2] }'>
    <source src="image.php?filename=myfile.mp4" type='video/mp4' />
</video>

可以在www / html / uploads,chmod 777内工作。完全没有错误。这是不好的做法:

<video id="my_video_1" class="video-js vjs-default-skin" width="100%" height="100%"
      controls preload="none" poster='img.png'
      data-setup='{ "playbackRates": [1, 1.5, 2] }'>
    <source src="uploads/myfile.mp4" type='video/mp4' />
</video>

你还想用mp4做些什么来阻止这种情况发生?

3 个答案:

答案 0 :(得分:2)

最好的方法是使用&#34;字节范围&#34; headers - 这只返回您需要的文件块。维基百科有一个非常简短的介绍(https://en.wikipedia.org/wiki/Byte_serving),但你可以谷歌更多。

这是我为我的项目编写的一项功能 - 您可能需要进行调整以使其符合您的确切需求,但它非常通用,可能会开箱即用。

function serve_file_resumable ($file, $contenttype = 'application/octet-stream') {

    // Avoid sending unexpected errors to the client - we should be serving a file,
    // we don't want to corrupt the data we send
    @error_reporting(0);

    // Make sure the files exists, otherwise we are wasting our time
    if (!file_exists($file)) {
        header("HTTP/1.1 404 Not Found");
        exit;
    }

    // Get the 'Range' header if one was sent
    if (isset($_SERVER['HTTP_RANGE'])) {
        $range = $_SERVER['HTTP_RANGE']; // IIS/Some Apache versions
    } else if ($apache = apache_request_headers()) { // Try Apache again
        $headers = array();
        foreach ($apache as $header => $val) {
            $headers[strtolower($header)] = $val;
        }
        if (isset($headers['range'])) {
            $range = $headers['range'];
        } else {
            $range = false; // We can't get the header/there isn't one set
        }
    } else {
        $range = false; // We can't get the header/there isn't one set
    }

    // Get the data range requested (if any)
    $filesize = filesize($file);
    if ($range) {
        $partial = true;
        list($param,$range) = explode('=',$range);
        if (strtolower(trim($param)) != 'bytes') { // Bad request - range unit is not 'bytes'
            header("HTTP/1.1 400 Invalid Request");
            exit;
        }
        $range = explode(',',$range);
        $range = explode('-',$range[0]); // We only deal with the first requested range
        if (count($range) != 2) { // Bad request - 'bytes' parameter is not valid
            header("HTTP/1.1 400 Invalid Request");
            exit;
        }
        if ($range[0] === '') { // First number missing, return last $range[1] bytes
            $end = $filesize - 1;
            $start = $end - intval($range[1]);
        } else if ($range[1] === '') { // Second number missing, return from byte $range[0] to end
            $start = intval($range[0]);
            $end = $filesize - 1;
        } else { // Both numbers present, return specific range
            $start = intval($range[0]);
            $end = intval($range[1]);
            if ($end >= $filesize || (!$start && (!$end || $end == ($filesize - 1)))) {
                $partial = false; // Invalid range/whole file specified, return whole file
            }
        }
        $length = $end - $start + 1;
    } else {
        $partial = false; // No range requested
        $length = $filesize;
    }



    // Send standard headers
    header("Content-Type: $contenttype");
    header("Content-Length: $length");  // was $filesize
    header('Content-Disposition: attachment; filename="'.basename($file).'"');
    header('Accept-Ranges: bytes');

    // if requested, send extra headers and part of file...
    if ($partial) {
        header('HTTP/1.1 206 Partial Content');
        header("Content-Range: bytes $start-$end/$filesize");
        if (!$fp = fopen($file, 'r')) { // Error out if we can't read the file
            header("HTTP/1.1 500 Internal Server Error");
            exit;
        }
        if ($start) {
            fseek($fp,$start);
        }
        while ($length) { // Read in blocks of 8KB so we don't chew up memory on the server
            $read = ($length > 8192) ? 8192 : $length;
            $length -= $read;
            print(fread($fp,$read));
        }
        fclose($fp);
    } else {
        readfile($file); // ...otherwise just send the whole file
    }

    // Exit here to avoid accidentally sending extra content on the end of the file
    exit;

  }

  serve_file_resumable ("../uploads/" . $filename, 'video/mp4');

答案 1 :(得分:1)

您需要将视频放到可供Web服务器访问的公共目录中(例如,在symfony框架中,它是目录web/uploads/),之后您就可以使用用于视频标记的src参数中的视频(正如您在第二个示例中所做的那样)。

如果你已经将视频放到了目录var/www/uploads的上传系统 - 你必须将这样的视频移动到公共目录,可能你已经使用了一些工作者或其他东西。
你这样做是因为从php流式传输视频内容 - 这是错误的方式......

此外,您不得将777 mod用于视频目录,必须使用755

答案 2 :(得分:1)

如果您使用的是Apache,则可以使用X-Sendfile标头来提供任何文件,包括不在公共可访问目录中的文件。

示例:

$filename = $_GET['filename'];
header('Content-Type: video/mp4');
header('X-Sendfile: ../uploads/'.$filename);

默认情况下通常不启用,因此您需要将其添加到httpd.conf

LoadModule xsendfile_module path/to/mod_xsendfile.so

根据需要调整路径并将其添加到.htaccess

XSendFile on

重启Apache,你很高兴。

此功能并非Apache独有。 事实上,这个想法来自Lighttpd。