لینک دانلود و پخش آنلاین ویدئو از storage لاراول
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 2 دقیقه

لینک دانلود و پخش آنلاین ویدئو از storage لاراول

دوستان سلام
قصد داریم فایل های ویدئویی ذخیره شده در پوشه storage رو پس از احراز هویت، برای دانلود و پخش در اختیار کاربر قرار دهیم.
برای دانلود ابتدا یک Route برای دانلود ایجاد میکنیم.

Route::get('/download/{workshop_id}/{topic_id}', 'Admin\FilesController@download')->name('download');

و به برای لینک دانلود از کد زیر استفاده میکنیم.

<a href="{{ Route('download' , [$Workshop->id,$topic->id]) }}">دانلود</a>

کنترلر ما هم به شکل زیر میشه، ابتدا Orders را بررسی میکنیم که آیا کاربر چنین محصولی را خریداری کرده یا خیر ، در صورت خریداری Topic که همان سرفصل مورد نظر از محصول هست را پیدا و به فایل دسترسی پیدا میکنیم و در نهایت با دستور Storage::download اقدام به دانلود میکنیم.
این دانلود فقط از طریق مرورگر امکان پذیر هست و نمیتوان با Adm و روش های دیگر دانلود کرد.

 public function download($workshop_id,$topic_id)
    {
        $order = Orders::where(['workshop_id' => $workshop_id , 'user_id' => Auth::user()->id])->first();
        if ($order) {
            switch ($order->order_status) {
                case 'پرداخت شده':
                    $is_accessible = true;
                    break;
                case 'پرداخت نشده':
                    $is_accessible = false;
                    break;
                default:
                $is_accessible = false;
                    break;
            }
        }else{
            dd('شما به این دوره دسترسی ندارید');
        }
        $topic = Topic::where(['workshop_id' => $workshop_id , 'id' => $topic_id])->first();
        if (!$topic) {
            dd('این دوره چنین فایلی ندارد');
        }
        if ($is_accessible && $topic) {
            $file_item = File::find($topic->file_id);
            return Storage::download($file_item->file_url);
        }else{
            dd('شما دسترسی به این فایل ندارید');
        }

    }

برای پخش آنلاین ویدئو یک Route مشخص میکنیم.

Route::get('videostream/{topic_id}', 'WorkShopLandingController@videoStream')->name('videostream');

برای پخش به شکل زیر عمل میکنیم.

<video>
    <source src="{{ Route('videostream' , $topic->id ) }}" type="video/mp4" />
</video>

و حالا میرسیم به کنترلر

 public function videoStream($topic_id){
        $topic = Topic::find($topic_id);
        if (!$topic) {
            abort(404);
        }
        if (Auth::user()) {
            $order = Orders::where(['workshop_id' => $topic->workshop_id , 'user_id' => Auth::user()->id])->first();
            if ($order) {
                switch ($order->order_status) {
                    case 'پرداخت شده':
                        $is_accessible = true;
                        break;
                    case 'پرداخت نشده':
                        $is_accessible = false;
                        break;
                    default:
                    $is_accessible = false;
                        break;
                }
            }else{
                $is_accessible = false;
            }
        }else{
            $is_accessible = false;
        }
        if ($is_accessible) {
            $file = File::find($topic_id);
            // $path = '/home/mdabbag/storage/upload/'.$file->file_url; //in server
            $path = Storage::url($file->file_url);
            if (!File::exists($path)) {
                abort(404);
            }
            $stream = new VideoStream($path);
            return response()->stream(function() use ($stream) {
                $stream->start();
            });
        }else{abort(404);}
    }

کنترلر بالا دوتا نکته مهم داره ، یک: path در سرور ، دو: VideoStream
اول VideoStream رو بررسی میکنیم.
در پوشه app و پوشه helpers یک فایل به اسم VideoStream ایجاد و کد زیر رو در اون قرار میدیم.

<?php
/**
 * Description of VideoStream
 *
 * @author Rana
 * @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($filePath)
    {
        $this->path = $filePath;
    }

    /**
     * Open stream
     */
    private function open()
    {
        if (!($this->stream = fopen($this->path, 'rb'))) {
            die('Could not open stream for reading');
        }

    }

    /**
     * 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->size  = filesize($this->path);
        $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) {
            $bytesToRead = $this->buffer;
            if(($i+$bytesToRead) > $this->end) {
                $bytesToRead = $this->end - $i + 1;
            }
            $data = fread($this->stream, $bytesToRead);
            echo $data;
            flush();
            $i += $bytesToRead;
        }
    }

    /**
     * Start streaming video content
     */
    function start()
    {
        $this->open();
        $this->setHeader();
        $this->stream();
        $this->end();
    }
}

برای اینکه helper بالا رو به لاراول بشناسونیم باید در پوشه config و فایل app.php در قسمت aliases خط آخرش این کد رو کپی کنیم.

 'VideoStream' => App\Helpers\VideoStream::class,

بعد این کار برای اینکه فایل app مجدد در لاراول لود بشه باید حتما دستور زیر را در ترمینال وارد کنیم.

composer dump-autoload 

و حالا در کنترلر مربوط به VideoStream به روش زیر helper رو use میکنیم.

use VideoStream;

تا اینجا تمام و روی لوکال کار میکند.
اما برای سرور آدرس path فایل های داخل پوشه storage به دلیل اینکه خارج از پوشه pablic قرار داره قابل شناسایی نیست، به همین دلیل باید به روش زیر عمل کنیم.

// $path = '/home/USER NAME/storage/upload/'.$file->file_url; //in server
// $path = '/home/mdabbag/storage/upload/'.$file->file_url; //in server

و در نهایت پخش ویدئو از storage در لوکال و سرور هندل میشه و لینک ویدئو از طریق Adm و ... در دسترس نیست.
البته میشه در هنگام پخش برای کاربر احراز هویت شده اون رو دانلود کرد.

حتما سوالات خودتون در این رابطه رو بپرسید، حتما اگر بدانم پاسخ میدم.

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
5 از 4 رای

/@mdabbagh
محمدسجاد دباغ
برنامه نویس لاراول

برنامه نویس Php Laravel و عاشق ایده پردازی و خلق پروژه هستم. با اصول کدنویسی تمیز ، دیزاین پترن ها ، restApi و کانفیگ سرور آشنایی خوبی دارم و علاقمند به شرکت در همایش ها، دورهمی ها، نمایشگاه های فناوری ، ارائه مطلب ، تدریس و توسعه دانش خود در زمینه طراحی بوم کسب و کار ، ایجاد استارتاپ و یادگیری بیشتر می‌باشم.

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید