1

我目前有这个代码:

function Stream($file)
{
header_remove(); 
$arr = get_headers($file);
foreach ($arr as &$value) {if((strpos($value,'Content-Type')!== false)){header($value);}}

    if (isset($_SERVER['HTTP_RANGE']))  {
        rangeDownload($file);
    }
    else {
header('HTTP/1.1 206 Partial Content');
header("Content-Length:1");
    //foreach ($arr as &$value) {if((strpos($value,'Content-Length')!== false)){header($value);}}
//header("Content-Range:bytes 21056-21056/243957100");
        readfile($file);
    }
}

function rangeDownload($file) {

    $fp = @fopen($file, 'rb');

    $size   = filesize($file); // File size
    $length = $size;           // Content length
    $start  = 0;               // Start byte
    $end    = $size - 1;       // End byte
    // 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.
     */
    header("Accept-Ranges: 0-$length");
    // header('Accept-Ranges: bytes');
    // multipart/byteranges
    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
    if (isset($_SERVER['HTTP_RANGE'])) {

        $c_start = $start;
        $c_end   = $end;
        // Extract the range string
        list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
        // Make sure the client hasn't sent us a multibyte range
        if (strpos($range, ',') !== false) {

            // (?) Shoud this be issued here, or should the first
            // range be used? Or should the header be ignored and
            // we output the whole content?
            header('HTTP/1.1 416 Requested Range Not Satisfiable');
            header("Content-Range: bytes $start-$end/$size");
            // (?) Echo some info to the client?
            exit;
        }
        // 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 ($range0 == '-') {

            // The n-number of the last bytes is requested
            $c_start = $size - substr($range, 1);
        }
        else {

            $range  = explode('-', $range);
            $c_start = $range[0];
            $c_end   = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
        }
        /* 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.
        $c_end = ($c_end > $end) ? $end : $c_end;
        // Validate the requested range and return an error if it's not correct.
        if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {

            header('HTTP/1.1 416 Requested Range Not Satisfiable');
            header("Content-Range: bytes $start-$end/$size");
            // (?) Echo some info to the client?
            exit;
        }
        $start  = $c_start;
        $end    = $c_end;
        $length = $end - $start + 1; // Calculate new content length
        fseek($fp, $start);
        header('HTTP/1.1 206 Partial Content');
    }
    // Notify the client the byte range we'll be outputting
    header("Content-Range: bytes $start-$end/$size");
    header("Content-Length: $length");

    // Start buffered download
    $buffer = 1024 * 8;
    while(!feof($fp) && ($p = ftell($fp)) <= $end) {

        if ($p + $buffer > $end) {

            // In case we're only outputtin a chunk, make sure we don't
            // read past the length
            $buffer = $end - $p + 1;
        }
        set_time_limit(0); // Reset time limit for big files
        echo fread($fp, $buffer);
        flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.
    }

    fclose($fp);

}

我可以看到所有 3 个请求都已发出,并且文件下载开始了,而不是视频屏幕出现流取消。我尝试模仿标准 apache 标头但没有成功,我试图让它以尽可能广泛的支持(例如内容类型检测)提供视频内容。

4

2 回答 2

0

问题出在范围下载功能中。在此功能中,您的标题是正确的。但是您正在打印完整的文件作为响应。

这些更改应该可以解决您的问题。

// Notify the client the byte range we'll be outputting
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: $length");

在这里,您需要在内容范围标头中查找文件以开始指定。

fseek($fp,$start) /* MISSING CODE */ 

插入上面的行应该可以解决您的问题。

// Start buffered download
$buffer = 1024 * 8;
while(!feof($fp) && ($p = ftell($fp)) <= $end) {

    if ($p + $buffer > $end) {

        // In case we're only outputtin a chunk, make sure we don't
        // read past the length
        $buffer = $end - $p + 1;
    }
    set_time_limit(0); // Reset time limit for big files
    echo fread($fp, $buffer);
    flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.
}

fclose($fp);
于 2013-07-27T16:36:36.453 回答
0

对于我被要求做的工作,我只需要经历同样的事情。我没有任何问题让它适用于 chrome/firefox,但我在 Mac/iPad 上遇到了 Safari 的问题。我参考了多个来源并尝试了很多东西,最后我放弃了我必须得到的一切,并决定尝试使用此问题中的代码解决问题,因为它与我已经拥有的非常相似,但不同之处足以让我更清楚。

这是使用原始问题中的代码作为基础但具有对我有用的必要修复的最终工作版本。希望这对其他人有帮助。

if (isset($_SERVER['HTTP_RANGE'])) {
  header('Content-Type: video/'.$ext); // reference your own mime variable here
  $fp = @fopen($filepath, 'rb');

  $size   = filesize($filepath); // File size
  $length = $size;           // Content length
  $start  = 0;               // Start byte
  $end    = $size - 1;       // End byte
  // 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.
   */
  header("Accept-Ranges: 0-$length");
  // multipart/byteranges
  // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
  $c_start = $start;
  $c_end   = $end;
  // Extract the range string
  list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
  // Make sure the client hasn't sent us a multibyte range
  if (strpos($range, ',') !== false) {
    // (?) Shoud this be issued here, or should the first
    // range be used? Or should the header be ignored and
    // we output the whole content?
    header('HTTP/1.1 416 Requested Range Not Satisfiable');
    header("Content-Range: bytes $start-$end/$size");
    // (?) Echo some info to the client?
    exit;
  }
  // 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 == '-') {
    // The n-number of the last bytes is requested
    $c_start = $size - substr($range, 1);
  }
  else {
    $range  = explode('-', $range);
    $c_start = $range[0];
    $c_end   = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
  }

  /* 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.
  $c_end = ($c_end > $end) ? $end : $c_end;
  // Validate the requested range and return an error if it's not correct.
  if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
    header('HTTP/1.1 416 Requested Range Not Satisfiable');
    header("Content-Range: bytes $start-$end/$size");
    // (?) Echo some info to the client?
    exit;
  }
  $start  = $c_start;
  $end    = $c_end;
  $length = $end - $start + 1; // Calculate new content length
  header('HTTP/1.1 206 Partial Content');
  // Notify the client the byte range we'll be outputting
  header("Content-Range: bytes $start-$end/$size");
  header("Content-Length: $length");

  // Start buffered download
  $buffer = 1024 * 8;
  fseek($fp,$start);
  while(!feof($fp) && ($p = ftell($fp)) <= $end) {
    if ($p + $buffer > $end) {
      // In case we're only outputtin a chunk, make sure we don't
      // read past the length
      $buffer = $end - $p + 1;
    }
    set_time_limit(0); // Reset time limit for big files
    echo fread($fp, $buffer);
    flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.
  }

  fclose($fp);
}

这是我使用的整个代码,除了用于构建文件路径和视频扩展名/mime 类型之前的一些变量。这已经在 Chrome(最新)、Firefox(最新)、Macbook Pro 上的 Safari 7.0.6、iOS 7 上的 Safari 上进行了测试

于 2015-02-05T21:19:20.580 回答