前回は、画像をパブリックに表示する方法を説明しましたが、今回は画像をプライベートに表示する方法です。
いくつか方法があります。
まず、前回のようにアップロードをパブリックの場所に保存して、特定のユーザーだけに表示のためのURLを教える。
しかし、DBから自動発行されるproduct_image_id
を画像ファイル名に使用するなら、URLを操作することで他のファイルも見れてしまいます。
そうなら、画像のURLをわかりにくいように変えて、他の画像のURLを予想しにくくすることも可能です。
例えば、235.jpg
とは見せずに、1f3870be274f6c49b3e31a0c6728957f.jpg
にするとか。
それは、md5()を利用することで簡単に可能です。
public function filename() { $ext = 'jpg'; switch($this->mime) { case 'image/jpeg': case 'image/jpg': $ext = "jpg"; break; case 'image/png': $ext = "png"; break; case 'image/gif': $ext = "gif"; break; } return sprintf("%d.%s", md5($this->product_image_id), $ext); }
よりセキュアにするには、DBに保存するときに、uniqid()あるいは、openssl-random-pseudo-bytes()を使用してランダムな値を生成して、その値をファイル名として保存するとか。要するに、IDのように連続な番号とはならないので、容易にファイル名を予測できないようにすることです。
しかし、究極は、ファイルをパブリックから見れない場所に保存して、それを表示する方法です。
例えば、storage/images/product/1.jpg
のように、パブリックから見れないstorage
のディレクトリに画像をアップロードするようにして、見せるときには、ログインしたユーザーと関連ある画像だけを、そのユーザーに表示する。
この場合は、パブリックに保存されている画像と違い、固定のURLを通してウェブサーバーに画像の表示を任せることはできません。逆に、あたかもウェブサーバーが画像ファイルを読んでデータをストリームするという作業と同じことをプログラムで行います。header()
を使用すれば、そう難しいことではありません。
namespace App\Http\Controllers\User; use Illuminate\Http\Request; use Log; use App\Http\Requests; use App\Http\Controllers\Controller; use App\Product; use App\ProductImage; class ProductController extends Controller { public function getImage(Product $product) { return view('user/product_image', compact('product')); } public function downloadImage(ProductImage $product_image) { //@TODO ここで、認証したユーザーに画像を表示していいかどうかをチェック。 //そうでないなら、空の画像を表示 $filename = $product_image->filename(); header("Content-type: $product_image->mime name=$filename"); header("Content-Disposition: attachment; filename=$filename"); header("Content-Length: ".@filesize($product_image->path)); header("Expires: 0"); @readfile($product_image->path); exit; } }
上で使用されるテンプレートは、
@extends('user.layouts.app') @section('content') <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <div class="panel panel-default"> <div class="panel-heading">アップロードしたファイルを表示</div> <div class="panel-body"> <div> @foreach ($product->product_images as $image) <img src="{{ url('/user/product_image', $image->product_image_id) }}"> @endforeach </div> </div> </div> </div> </div> </div> @endsection
のようになります。
routes.php
は、以下のように認証保護された中でコールされます。
Route::group(['prefix' => 'user', 'middleware' => 'web'], function () { Route::get('login', 'User\Auth\AuthController@showLoginForm'); Route::post('login', 'User\Auth\AuthController@login'); Route::get('logout', 'User\Auth\AuthController@logout'); .. Route::group(['middleware' => 'auth:user' ], function () { Route::get('home', 'User\HomeController@index'); .. Route::get('product/{product}/image', 'User\ProductController@getImage'); Route::get('product_image/{product_image}', 'User\ProductController@downloadImage'); }); });メルマガ購読の申し込みはこちらから。