laravel標準バリデートのmimetypesだと、video/*にしても、video/3gppなどが引っかかったり、拡張子AMRなどMIME取得できないファイルをOKにしたかったので、独自バリデートを実装してみた。
以前の記事でいけるかと思ったら無理だった…。
video/*にしても、video/3gppなどが引っかかるのが謎なんだよね・・・。
たいていの音声・動画ファイルは上手くいくけど、たまにMIME取得できているのにバリデーションエラーになるのは何故だんだろ?laravelのバグ?
原因究明するよりも、独自バリデーションを実装した方が早そう。拡張子AMRなどMIME取得できないファイルもOKにしたいし。
独自バリデーションルールを追加する時は、app/Providers/AppServiceProvider.phpのboot()に、Validator::extendを追加する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { // laravel標準のmimetypesだと、video/*にしても、video/3gppなどが引っかかってしまうので、独自バリデートを実装 \Validator::extend('uplaod_mimetypes', function($attribute, $value, $parameters, $validator) { // $mimetype = $value->getClientMimeType(); // mimetype // $extension = $value->getClientOriginalExtension(); // 拡張子 // クライアントの自己申告は信用せず、PHPでMIME判定する $mimetype = mime_content_type($value->getRealPath()); // テキストとPDFはOK if($mimetype == 'text/plain' || $mimetype == 'application/pdf' ){ return true; } // 画像・音声・映像もOK if( preg_match("/^image\/.+$/u", $mimetype) || preg_match("/^audio\/.+$/u", $mimetype) || preg_match("/^video\/.+$/u", $mimetype) ){ return true; } // 音声ファイルの拡張子amrはコメントアウトされているので、マジックナンバーで判定する。 // https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types // apacheのconf/mime.typesが修正できるなら、コメントアウト外せば行ける? //先頭数文字を取得する.巨大なファイルでも先頭だけ見る(amrファイルのマジックナンバーASCIIは、#!AMR) $magic = file_get_contents($value->getRealPath(), false, null, 0, 5); if($mimetype == 'application/octet-stream' && strpos($magic, '#!AMR') === 0 ){ return true; } // 以上に当てはまらなければ、バリデーションエラー return false; }); } } |
app/Http/Controller/HomeController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$messages = [ 'upload_file.uplaod_mimetypes' => $request->file('upload_file')->getClientOriginalName() .'はアップロードできません。', 'upload_file.max' => $request->file('upload_file')->getClientOriginalName() .'はアップロードできません。', ]; $renames = [ // 'name' => '名前', ]; // 第1引数 = $request(postデータ) // 第2引数 = バリデーション・ルール // 第3引数 = name.ruleの上書きの文字列 // 第4引数 = nameの上書きの文字列 $this->validate($request, [ 'upload_file' => [ 'file' => 'max:51200', // 最大50MBまで // プレーンテキスト、PDF、画像ファイル、音声ファイル、動画ファイルのみOK! 'uplaod_mimetypes' ], ], $messages, $renames ); |
ダウンロード処理のMIMEも、ちゃんと指定するようにする(古い機種だと application/octet-stream では認識できないみたい)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// アップロードされたファイルをダウンロードする public function download($download_file) { // ハッシュ値から、ファイル名を取得する $report_detail = Report_detail::where('upload_file_hash', $download_file)->first(); $original_filename = $report_detail->upload_file_name; // ダウンロード対象ファイル $upload_filepath = storage_path() . '/app/public/' . rawurlencode($download_file); // サーバ上のファイルからMIME判定 $mimetype = mime_content_type($upload_filepath); // amrファイルはapacheでコメントアウトされているのでMIME判定できないので、ファイル先頭のマジックナンバーで判断する $magic = file_get_contents($upload_filepath, false, null, 0, 5); if($mimetype == 'application/octet-stream' && strpos($magic, '#!AMR') === 0 ){ $mimetype = 'audio/amr'; } // レスポンスヘッダで、ファイル名やMIMEを指定する! $headers = array( // 'Content-Type' => "application/octet-stream", // どんなファイルか分からない時は、application/octet-streamを指定する 'Content-Type' => $mimetype, // サーバ上のファイルからMIME判定 'Content-Disposition' => 'attachment; filename="' . $original_filename . '"' ); return \Response::download($upload_filepath, $original_filename, $headers ); } |