Laravelを使って動画配信する方法(ダウンロード可)

さてさて、すでにインターネットがマルチメディア化してから相当時間が経ち、当時はまだ珍しかった(また、視聴にストレスが高かった😅)動画の存在はなくてはならないものとなりました。

そして、動画はいまや誰でも持っているスマートフォン付属のカメラでいつでも撮影ができるため、撮影したものを個別にアップロードするウェブサービスも珍しいものではなくなっています。

ちなみにLaravelで最もシンプルに動画を配信する方法は?というと、publicフォルダに動画を設置し直接ウェブサーバーを通してアクセスすれば問題ありません。

ただし、ここである機能を追加するとなると1手間必要になってきます。それが、

許可されたユーザーだけに動画を公開する

というものです。

つまり、先ほど書いたpublicフォルダに入れてしまうと誰でも見ることができますが、そうではなく例えばアップロードしたユーザーだけが見ることができるように制限をかけたい場合です。しかも、動画データが全てダウンロードされてからしか再生されないとなると不便でしょうがありませんので、少しずつコンテンツを取得しながら再生できるようにしてみます。

ということで、今回はLaravelを使って制限つき動画公開の方法を紹介します。
ぜひ皆さんのお役にたてると嬉しいです。(最後に今回使用したコード一式をダウンロードすることができます)

開発環境: Laravel 5.8

やりたいこと

次の条件を満たすようなコードを作っていきます。

  • Laravel認証にログインしたユーザーだけが動画を再生できる
  • 動画データのダウンロードが完了していなくても再生ができる

実現する方法

コードを見たい場合は動画配信する機能をつくるまで読み飛ばしてください。

動画をLaravel(PHP)で読み出して配信する場合、状況によって2つの場合があります。

1つめは、最初から再生する(初めてその動画にアクセスしてきた)場合。この場合はその動画全体を配信するだけですので、Laravelを使えばそれほど難しい処理ではありません。

そして、2つ目は途中から再生する場合です。この場合は、指定された場所から再生されないといけないわけですから、ヘッダー部分にそれ相応の情報を格納する必要があります。

では実際のコードを見てみましょう!

動画配信する機能をつくる

基本的な準備

※ 前提としてphp artisan make:authでLaravel認証をインストールしているものとします。もし、まだ実施していない場合は【Laravel5.6】インストール直後にやること3点を参照してください。

まずLaravelにブラウザからアクセスできるようルーティングとコントローラーを作成します。

(ルーティング)

Route::get('video/stream', 'VideoController@stream');

(コントローラー)

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class VideoController extends Controller
{
    public function stream(Request $request) {

        // ここに動画配信コードを書く

    }
}

そして、今回重要となる機能「ログインしていたら動画再生OK」という部分をmiddlewareを使って実装します。

ミドルウェアはルーティングの中でも指定できますが、今回はシンプルにコントローラーから呼び出します。

class VideoController extends Controller
{
    public function __construct() {

        $this->middleware('auth');

    }

これで、https://*****.com/video/streamにログインせずアクセスすると、以下のようにログインフォームにリダイレクトされるようになります。

動画配信の部分を作っていく

では、ここからがメインとなる動画データの配信部分になります。

まずはコードから。
ちなみに今回はテストなので、video/test.mp4という動画を使います。

public function stream(Request $request) {

    $path = storage_path('video/test.mp4');

    $file_size = filesize($path);
    $fp = fopen($path, 'rb');
    $status_code = 200;
    $headers = [
        'Content-type' => 'video/mp4',
        'Accept-Ranges' => 'bytes',
        'Content-Length' => $file_size
    ];

    $range = $request->header('Range');

    if(!is_null($range)) {

        if(preg_match('|bytes=([0-9]+)\-|', $range, $matches)) {

            $start = intval($matches[1]);

            if(fseek($fp, $start) === 0) {

                $status_code = 206;
                $headers['Content-Length'] = $file_size - $start;
                $headers['Content-Range'] = sprintf(
                    'bytes %d-%d/%d',
                    $start,
                    ($file_size-1),
                    $file_size
                );

            }

        }

    }

    return response()->stream(function() use($fp) {

        fpassthru($fp);

    }, $status_code, $headers);

}

やっていることは、まず再生したい動画でfopen()で開き、ファイルのサイズや基本となるレスポンス・ヘッダーを用意します。

そして、重要なのが$request->header('Range');の部分です。
この部分ではブラウザが送信してきた「Range」という名前のヘッダーを取得しているのですが、もしここがnullではない場合は、「途中からデータをください」という意味になりますので、返す動画データもそれに合わせて変更しなければいけません。

そこでif文でチェックをし、もし動画を部分的に要求するフォーマットにマッチしていたら、以下2つのヘッダーを用意することになります。

  • Content-Length ・・・ データの長さ(つまり、ファイルサイズ – 開始位置)
  • Content-Range ・・・ データの範囲(開始位置、終了位置、フルの長さ)が分かるデータ

そして、最終的にLaravelのresponse()->stream()メソッドを通してPHP標準関数のfpassthru()を実行すれば完成です。

※ ちなみにヘッダー情報のContent-type-typeは今回固定で「video/mp4」にしていますが、動画によって切り替えてください。

ちなみに

もし、アップロードした本人だけ動画を再生できるようにしたい場合はGateを使うと便利です。実際に作ってみましょう。

まず、app/Providers/AuthServiceProvider.phpを開いてboot()内に以下のようなコードを追加します。

public function boot()
{
    $this->registerPolicies();

    //

    \Gate::define('play-video', function ($user, $video) {

        return ($user->id === $video->user_id);

    });
}

これは、アップロードした際にDBに保存されたユーザーIDと、現在ログイン中のユーザーIDが一致するかどうかをチェックするGateです。(つまり、アップロードした人かどうかのチェックですね)

そして、このGateを使うには以下のようにします。

public function stream(Request $request) {

    $video = \App\Video::first();

    if (\Gate::allows('play-video', $video)) {

        // ここでさっきの動画配信コードを実行する

    }

以上です!

ダウンロード

今回実際に開発したソースコード一式を次からダウンロードすることができます。

Laravelを使って動画配信する

おわりに

ということで、今回はLaravelで動画を配信する方法をお届けしました。

今回の方法を使えば、サイトに動画配信する機能をフレキシブルにつけることができると思います。

ただし、当然ですが動画ファイルというのは画像やテキストと比べてずいぶんファイルサイズが大きいことが常です。

そのため、サーバー環境によってはffmpegなどで動画を小さく変換したり、外部ストレージに保存しておくなどの工夫をしないとサーバーが頻繁にダウンしてしまうことになりかねませんので気をつけてくださいね。

ということで今回はここまでです。

ではでは〜!

この記事が役立ちましたらシェアお願いします😊✨