ファイルをプログラムを動かすマシンの外に置くことがよくあります。そういったところにあるファイルのMIMEタイプを得たい時があります。これを比較的高速に行う方法を紹介します。
ファイルの種類の識別は通信相手と保存されているファイルを信用するなら拡張子の識別でも大丈夫です。もし信用しないのであればファイルの中身を元にMIMEタイプを得る必要がある増す。高速化するためにはMIMEタイプの識別に十分なだけの量を読むのが望ましいです。とりあえず先頭100バイトも読めば識別できるので100バイトだけ読むことにします。この外部ファイルの先頭100バイトを読んでMIMEタイプを得る実際のコードが次です。
<?php
/**
* URL から mime type を取得する
* @param string $url
* @return array{string|null, string|null} [mime, error]
*/
function get_mime_from_url(string $url): array
{
$ch = curl_init();
// cURLオプションを設定
curl_setopt($ch, CURLOPT_URL, $url); // URLを指定
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 実行結果を文字列として返す
// ファイルの先頭100バイトだけを取得
curl_setopt($ch, CURLOPT_RANGE, '0-99');
// データを取得
$content = curl_exec($ch);
// cURLセッションを終了
curl_close($ch);
// 取得に失敗した場合はエラーを返す
if($content === false) {
return [null, curl_error($ch)];
}
// MIMEタイプを推測
// 取得できたファイルの先頭100バイトを元にfileinfoを使ってMIMEタイプを取得
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_buffer($finfo, $content);
finfo_close($finfo);
return [$mime, null];
}
// 使用例
$url = 'http://localhost/test.mp4';
[$mime, $err] = get_mime_from_url($url);
var_dump($mime); // string(9) "video/mp4"
var_dump($err); // NULL
cURLを使って、ファイルの内容を一部読み取り、それをFileinfoにかけてMIMEタイプを取得しています。このコードはファイルの内容を先頭の一部のみ読むコードと、その一部のみでMIMEタイプを得るコードに別れています。ファイルを読む部分についてはcURLに限らず何がしかのファイル関連の機能でも全然使えます。例えばLaravelで使われているファイルシステムのクラスには必ずreadStreamメソッドが生えています(インターフェースでそう定義されています)。これを利用して独自の処理でMIMEタイプを得ることもできます。
// ファイルシステムのreadStreamメソッドからresourceを得る
$stream = \Storage::disk('dropbox')->readStream($path);
// resourceから100バイトだけ読む
$content = stream_get_contents($stream, 100);
// 前述のコード同様にMIMEタイプを得る
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_buffer($finfo, $content);
finfo_close($finfo);
こんな感じで比較的高速に外部にあるファイルのMIMEタイプを確認できます。更なる高速化を図る場合は通信先のサーバーが許す範囲で非同期処理による並列アクセスを行う方法があります。PHPの処理自体は並列化できませんが、通信を非同期にすることで通信待ちの間に処理を進めたり、複数の通信を同時に行ったりすることができ、より高速になります。これを実装するのであれば spatie/async というライブラリが便利です。
spatie/async: Easily run code asynchronously