PHPを用いてWebアプリケーションを開発する際、サーバー上に保存されたファイルのサイズを取得する処理は非常に頻繁に発生します。
ユーザーがアップロードした画像のファイルサイズを制限したり、ダウンロード機能で正確なContent-Lengthをヘッダーに含めたりと、その用途は多岐にわたります。
しかし、PHPにはファイルサイズを取得するための方法が複数存在し、状況に応じて最適な手法を選択しなければなりません。
特に大容量のファイルを扱う場合や、ネットワーク経由でリモートファイルの状態を確認する場合には、標準的な関数だけでは不十分なケースもあります。
本記事では、PHPでファイルサイズを正確に取得するための基本から、現場で役立つ実践的なテクニックまでを詳しく解説します。
初心者の方から、パフォーマンスやセキュリティを意識する中級者の方まで、幅広く活用いただける内容となっています。
PHPでファイルサイズを取得する基本:filesize関数
PHPでローカルサーバー上のファイルサイズを取得する最も一般的な方法は、filesize()関数を使用することです。
この関数は、引数に対象ファイルのパスを文字列として渡すだけで、そのサイズをバイト(bytes)単位で返却します。
基本的な使い方は非常にシンプルであり、数行のコードで実装することが可能です。
<?php
// ファイルパスを指定します
$filePath = 'example.txt';
// ファイルが存在するか確認した上でサイズを取得します
if (file_exists($filePath)) {
$size = filesize($filePath);
echo "ファイルサイズ: " . $size . " バイト";
} else {
echo "ファイルが見つかりません。";
}
?>
ファイルサイズ: 1234 バイト
filesize()関数を使用する際の注意点として、返り値は整数型(int)となりますが、PHPのバージョンやOSの環境によって最大値が異なる点が挙げられます。
また、ファイルが存在しない場合や読み取り権限がない場合には、falseを返し、さらにE_WARNINGレベルのエラーが発生します。
そのため、実務では必ずfile_exists()やis_readable()を併用して、事前にチェックを行うのが定石です。
実行結果のキャッシュとclearstatcache関数
PHPのファイルシステム関数は、パフォーマンスを向上させるために、特定の関数の実行結果を内部的にキャッシュする仕組みを持っています。
filesize()もその対象であり、同じスクリプトの実行中に同一ファイルのサイズを何度も取得しようとすると、古い情報を返してしまう可能性があります。
例えば、スクリプトの途中でファイルにデータを書き込み、その直後にサイズを再取得するようなケースでは注意が必要です。
このような場合には、clearstatcache()関数を呼び出すことで、キャッシュを強制的にクリアできます。
<?php
$file = 'test.log';
// 最初のサイズ取得
echo "初回: " . filesize($file) . " バイト\n";
// ファイルにデータを追記します
file_put_contents($file, "追加データ", FILE_APPEND);
// キャッシュをクリアせずに再取得(古いサイズが返る可能性がある)
echo "クリア前: " . filesize($file) . " バイト\n";
// キャッシュをクリアしてから再取得
clearstatcache();
echo "クリア後: " . filesize($file) . " バイト\n";
?>
このように、動的にファイル内容が変化する処理を含む場合は、clearstatcache()を適切に挟むことが正確な計測の鍵となります。
取得したファイルサイズの単位変換(KB/MB/GB)
filesize()関数が返すバイト単位の数値は、人間にとっては直感的に分かりにくいものです。
一般的には、バイトを1024で割ることで、キロバイト(KB)、メガバイト(MB)、ギガバイト(GB)へと変換して表示します。
以下に、どのような数値でも適切な単位に自動変換する汎用的な関数を紹介します。
<?php
/**
* バイト数を読みやすい形式にフォーマットする
* @param int $bytes
* @param int $precision 小数点以下の桁数
* @return string
*/
function formatFileSize($bytes, $precision = 2) {
$units = array('B', 'KB', 'MB', 'GB', 'TB');
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, $precision) . ' ' . $units[$pow];
}
$size = 10485760; // 10MB
echo formatFileSize($size);
?>
10 MB
このコードでは、対数関数log()を利用して、値がどの単位に該当するかを計算しています。
これにより、数テラバイトに及ぶような大容量ファイルでも、動的に最適な単位を付与することが可能です。
ユーザーインターフェースにファイルサイズを表示する際は、このような変換ロジックを共通関数として持っておくことを強く推奨します。
2GBを超える大容量ファイルへの対応
PHPで大容量ファイルを扱う際、避けて通れないのが「2GBの壁」と呼ばれる問題です。
これは、32bit環境のPHPにおいて、整数型(integer)が符号付き32bitとして扱われることに起因します。
32bit環境では最大値が2,147,483,647バイト(約2GB)となり、これを超えるファイルサイズをfilesize()で取得しようとすると、負の数値が返ったり、正しく取得できなかったりする不具合が生じます。
64bit環境のPHPを利用する
現在の主要なサーバー環境(PHP 8.x以降かつ64bit OS)であれば、この問題は大幅に緩和されています。
64bit環境であれば、整数型は64bit(約900京バイトまで)を扱えるため、実質的にどのような巨大ファイルでもfilesize()で正確に計測できます。
まずは、自身が使用している環境が64bitであるかどうかを確認しましょう。
<?php
echo PHP_INT_SIZE; // 8 であれば 64bit、4 であれば 32bit
?>
32bit環境での代替手段(cURLやOSコマンド)
もし制約により32bit環境で大容量ファイルを扱わなければならない場合、外部コマンドを利用する回避策があります。
例えば、Linux環境であればlsやstatコマンドを呼び出し、その実行結果を文字列として受け取る手法です。
<?php
$filePath = 'large_file.iso';
// 外部コマンド stat を利用してサイズを取得(Linux/Unix環境)
$size = exec("stat -c %s " . escapeshellarg($filePath));
echo "大容量ファイルサイズ: " . $size . " バイト";
?>
ただし、外部コマンドの実行はサーバーの環境に依存し、セキュリティ上のリスク(コマンド注入)も伴うため、escapeshellarg()による対策を忘れないでください。
可能な限り、インフラ環境自体を64bitへ移行することが、最も安全で根本的な解決策となります。
SPL(Standard PHP Library)を利用した取得方法
PHPには、オブジェクト指向でファイル情報を操作するためのSplFileInfoクラスが用意されています。
filesize()関数を単体で使うよりも、ファイルに関するさまざまな情報をまとめて取得したい場合に非常に便利です。
モダンなPHP開発(LaravelやSymfonyなどのフレームワークを利用する場合を含む)では、こちらの利用が推奨されることも多いです。
<?php
$fileInfo = new SplFileInfo('example.txt');
if ($fileInfo->isFile()) {
$size = $fileInfo->getSize();
$extension = $fileInfo->getExtension();
echo "ファイル名: " . $fileInfo->getFilename() . "\n";
echo "サイズ: " . $size . " バイト\n";
echo "拡張子: " . $extension;
}
?>
SplFileInfo::getSize()メソッドは、内部的にfilesize()と同様の処理を行いますが、オブジェクトとして情報を持ち回ることができるため、コードの可読性や保守性が向上します。
また、ファイルが存在しない場合にインスタンス化自体は成功しますが、getSize()を呼び出した時点で例外(RuntimeException)が発生するため、より堅牢なエラーハンドリングが可能です。
リモートファイルのサイズを効率的に取得する
自分のサーバー内ではなく、外部サーバーにあるファイルのサイズを取得したい場合、filesize()関数は(設定により可能な場合もありますが)基本的に推奨されません。
allow_url_fopenが有効であればURLを渡すことも可能ですが、ファイル全体をダウンロードしようとする動作が含まれるため、非常に低速でメモリを消費します。
外部ファイルのサイズを確認するには、HTTPのHEADリクエストを送信し、レスポンスヘッダーのContent-Lengthを確認するのが効率的です。
get_headers関数を利用する方法
最も手軽なのはget_headers()関数を利用する方法です。
<?php
$url = 'https://example.com/image.jpg';
$headers = get_headers($url, 1);
if (isset($headers['Content-Length'])) {
$size = $headers['Content-Length'];
echo "リモートファイルサイズ: " . $size . " バイト";
}
?>
ただし、サーバーによっては配列のキーのケース(大文字小文字)が異なる場合や、複数のContent-Lengthが返ってくる場合があるため、パース処理には注意が必要です。
cURLを利用した高度な取得
より確実かつ高速に取得したい場合は、cURLライブラリを利用します。
CURLOPT_NOBODYオプションを有効にすることで、ボディをダウンロードせずにヘッダー情報のみを取得できます。
<?php
$url = 'https://example.com/large-archive.zip';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true); // ボディをダウンロードしない
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // リダイレクトを追跡
curl_exec($ch);
$size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
curl_close($ch);
if ($size != -1) {
echo "リモートファイルサイズ: " . $size . " バイト";
} else {
echo "サイズを取得できませんでした。";
}
?>
この手法であれば、数GBあるような外部ファイルであっても、数ミリ秒でサイズのみを把握することが可能です。
エラーハンドリングとセキュリティの注意点
ファイルサイズ取得処理を実装する上で、避けては通れないのが権限とセキュリティの問題です。
まず、PHPを実行しているユーザー(Webサーバーの実行ユーザーなど)に、対象ファイルへの読み取り権限(Read Permission)があることを確認してください。
権限がない場合、関数は失敗し、ログに警告が出力されます。
| 確認項目 | 推奨される関数 | 理由 |
|---|---|---|
| 存在確認 | file_exists | 存在しないファイルへのアクセスエラーを防ぐため。 |
| 読み取り権限 | is_readable | パーミッションエラーを事前に回避するため。 |
| パスの安全性 | realpath | ディレクトリトラバーサル攻撃を防ぐため。 |
特に、ユーザーから入力されたファイル名をそのままfilesize()に渡すのは非常に危険です。
../../etc/passwdのような相対パスを悪用されないよう、必ずbasename()でファイル名のみを取り出すか、許可されたディレクトリ配下であることをチェックする処理を組み込んでください。
また、ファイル操作は稀に失敗する可能性があるため、エラー制御演算子(@)で警告を抑制するのではなく、条件分岐をしっかり行うコード設計が望まれます。
まとめ
PHPでファイルサイズを取得する方法は、単にfilesize()関数を使うだけでなく、多角的な視点が必要です。
基本となるfilesize()は非常に高速ですが、キャッシュの影響を受けるため、連続して値が変化する処理ではclearstatcache()を忘れないようにしましょう。
また、2GBを超えるファイルを扱う際は、実行環境が64bitであるかを確認し、必要に応じて代替手段を検討してください。
ユーザーにサイズを提示する場面では、単位変換関数を導入することで、利便性の高いインターフェースを提供できます。
さらに、リモートファイルのサイズ取得にはcURLを活用するなど、対象がローカルかネットワーク越しかに応じて最適なツールを使い分けてください。
これらのテクニックを組み合わせることで、エラーに強く、パフォーマンスの高いPHPアプリケーションを構築できるようになります。
正しいファイル操作の知識を身につけ、日々の開発業務に活かしていきましょう。
