九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、すでにインターネットがマルチメディア化してから相当時間が経ち、当時はまだ珍しかった(また、視聴にストレスが高かった😅)動画の存在はなくてはならないものとなりました。
そして、動画はいまや誰でも持っているスマートフォン付属のカメラでいつでも撮影ができるため、撮影したものを個別にアップロードするウェブサービスも珍しいものではなくなっています。
ちなみに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
などで動画を小さく変換したり、外部ストレージに保存しておくなどの工夫をしないとサーバーが頻繁にダウンしてしまうことになりかねませんので気をつけてくださいね。
ということで今回はここまでです。
ではでは〜!