在没有将文件写入文件系统的情况下,将文件流传输到PHP中的html5

时间:2018-03-27 23:15:47

标签: php video streaming

我对PHP很新,对于流式视频来说肯定是新手。

我的mp4视频存储在我的SQL SERVER的文件流列中,我想将它们流式传输到HTML5视频。

我设法使用以下流类(http://codesamplez.com/programming/php-html5-video-streaming-tutorial)通过'file_put_contents($ filePath,$ queryresult)提供临时文件',但不得不依赖于写入然后清理中间件文件系统中的文件最终会出现问题。

我是否错过了一个关键概念,它允许我直接从数据库流式传输文件到视频“src”而无需写入其间的文件?

提前致谢!

3 个答案:

答案 0 :(得分:0)

如果您自己创建临时文件,然后将其删除。然后,您可以使用内置的PHP来改进该过程 <{1}}

  

以读写(w +)模式创建具有唯一名称的临时文件,并返回文件句柄。文件在关闭时自动删除(例如,通过调用fclose(),或者没有剩余的引用tmpfile()返回的文件句柄,或者脚本结束时。

tempfile()

所以现在你对temp没有责任。删除PHP将为您处理。如果您需要自定义临时。文件名或获取您可以使用此http://php.net/manual/en/function.sys-get-temp-dir.php

的详细信息

如果您根本不想使用文件,而是希望纯粹使用内存解决方案,那么您可以尝试使用内存流

private function open()
{
    if ( !($this->stream = tmpfile()) ) {
        fwrite($this->stream, $videodata);
        rewind($this->stream);
        die('Could not open stream for reading');
    }
    // now your temp file is ready to be read.
} 

您可以使用tcp流在内存流上fread,fwrite,file_get_contents或将其丢弃到网络上。但我必须说第二种解决方案是内存密集型,因此不适合流式传输大型文件。

答案 1 :(得分:0)

写一个临时文件对我来说似乎是一个临时的解决方法。当您从数据库获取流并且需要向客户端提供流时,根本不需要在硬盘上存储数据。

您需要做的就是替换从文件中读取的链接示​​例部分(并将二进制内容回显给客户端)并将其从sql中读取。

在链接的示例函数stream()中,替换此部分

    while(!feof($this->stream) && $i <= $this->end) {
        $bytesToRead = $this->buffer;
        if(($i+$bytesToRead) > $this->end) {
            $bytesToRead = $this->end - $i + 1;
        }
        $data = fread($this->stream, $bytesToRead);
        echo $data;
        flush();
        $i += $bytesToRead;
    }

替换为这样的东西:

/* Execute the query. */  
$stmt = sqlsrv_query($conn, $tsql, $params);  
if( $stmt === false )  
{  
     echo "Error in statement execution.</br>";  
     die( print_r( sqlsrv_errors(), true));  
}  

/* Retrieve and display the data.  
The return data is retrieved as a binary stream. */  
if ( sqlsrv_fetch( $stmt ) )  
{  
   $videostream = sqlsrv_get_field( $stmt, 0,   
                      SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY));  
   //header("Content-Type: image/jpg");  
   fpassthru($videostream );  
}  
else  
{  
     echo "Error in retrieving data.</br>";  
     die(print_r( sqlsrv_errors(), true));  
}  

答案 2 :(得分:0)

为了后代,这是我的最终结果,似乎不需要临时文件也可以很好地工作。

不确定将查询包含到类中并仅将$ MediaFileID传递给类是否更有意义,但这是按原样进行的,因此我现在将其保留。

整个修改后的类均包含其原始信用信息:

function ExecutereadMediaSP($MediaFileID){

    try{

    $connection = ConnectToDB();

    ini_set('memory_limit', '-1');

    // logs basic info about media viewer.  Mainly for a basic hit counter.
    LogMediaRequest($connection, $MediaFileID);

    //Selects binary data from SQL Server based on MediaFileID.  Passes this to Stream Class.  May be able to make further improvements later.
    $sql = "SELECT ... data from FILESTREAM column based on file ID ...";   

    $rst = $connection->prepare($sql);
    $rst->execute();
    $rst->bindColumn(1, $filecontent, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);

    $row = $rst->fetch(PDO::FETCH_ASSOC);//sql can only return one row due to unique identifier

    //Stream file 
    $stream = new VideoStream($filecontent);
    $stream->start();

    //Clean up.
    $rst->closeCursor();
    unset($rst);
    $connection = null;

    } catch (Exception $e) {
        error_log("Error in getting video\n".$e->getMessage(),0);
    }

}


/**
 * VideoStream - PHP class that supports (adaptive) streaming of files
 *
 * @author Rana
 * modified by HazCod to use stream_get_contents and correct session shutoff
 * https://github.com/HazCod
 * @link http://codesamplez.com/programming/php-html5-video-streaming-tutorial
 */
class VideoStream
{
    private $path = "";
    private $stream = "";
    private $buffer = 102400;
    private $start  = -1;
    private $end    = -1;
    private $size   = 0;

    function __construct($filecontent) 
    {


        try{

        // Opens file handle resource to replace use of actual file in the file system.
        $file_handle = fopen('php://memory', 'r+', false, stream_context_create()); 

        // Writes data from SQL Query to file wrapper.
        fwrite($file_handle, $filecontent);

        // Moves pointer to beginning of file.
        fseek($file_handle, 0);

        // Gets info on the "file."  Required to get filesize.
        $fstat = array();

        // gather statistics
        $fstat = fstat($file_handle);

        //Set File Size for Stream Class.
        $this->size  = $fstat['size'];

        // Define Stream as "File."
        $this->stream = $file_handle;

        } catch (PDOException $e) {
            error_log("Error in getting video\n".$e->getMessage(),0);
        }

    }

    /**
     * Set proper header to serve the video content
     */
    private function setHeader()
    {   
        ob_get_clean();
        header("Content-Type: video/mp4");
        header("Cache-Control: max-age=2592000, public");
        header("Expires: ".gmdate('D, d M Y H:i:s', time()+2592000) . ' GMT');
      //  header("Last-Modified: ".gmdate('D, d M Y H:i:s', @filemtime($this->path)) . ' GMT' );
        $this->start = 0;

        $this->end   = $this->size - 1;
       // header("Accept-Ranges: 0-".$this->end);

        if (isset($_SERVER['HTTP_RANGE'])) {

            $c_start = $this->start;
            $c_end = $this->end;

            list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
            if (strpos($range, ',') !== false) {
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                header("Content-Range: bytes $this->start-$this->end/$this->size");
                exit;
            }
            if ($range == '-') {
                $c_start = $this->size - substr($range, 1);
            }else{
                $range = explode('-', $range);
                $c_start = $range[0];

                $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $c_end;
            }
            $c_end = ($c_end > $this->end) ? $this->end : $c_end;
            if ($c_start > $c_end || $c_start > $this->size - 1 || $c_end >= $this->size) {
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                header("Content-Range: bytes $this->start-$this->end/$this->size");
                exit;
            }
            $this->start = $c_start;
            $this->end = $c_end;
            $length = $this->end - $this->start + 1;
            fseek($this->stream, $this->start);
            header('HTTP/1.1 206 Partial Content');
            header("Content-Length: ".$length);
            header("Content-Range: bytes $this->start-$this->end/".$this->size);
        }
        else
        {
            header("Content-Length: ".$this->size);
        }  

    }

    /**
     * close curretly opened stream
     */
    private function end()
    {
        fclose($this->stream);
        exit;
    }

    /**
     * perform the streaming of calculated range
     */
    private function stream()
    {
        $i = $this->start;
        set_time_limit(0);
        while(!feof($this->stream) && $i <= $this->end && connection_aborted() == 0) {
            $bytesToRead = $this->buffer;
            if(($i+$bytesToRead) > $this->end) {
                $bytesToRead = $this->end - $i + 1;
            }
            $data = stream_get_contents($this->stream, $bytesToRead);
            echo $data;
            flush();
            $i += $bytesToRead;
        }
    }

    /**
     * Start streaming video content
     */
    function start()
    {
        session_write_close(); //ensure our session is written away before streaming, else we cannot use it elsewhere
        $this->setHeader();
        $this->stream();
        $this->end();
    }
}