【PHP】ファイルサイズを取得してKB/MB/GBに自動変換して表示する方法

【PHP】ファイルサイズを取得してKB/MB/GBに自動変換して表示する方法 PHP

ダウンロードリンクや管理画面に「1.2 MB」のような読みやすいサイズを表示したいときは、filesize() でバイト数を取得し、単位変換用の関数で KB / MB / GB へ自動変換するのが定石です。ここでは実運用でそのまま使える汎用関数と、注意点、ディレクトリの合計サイズまでをまとめます。

基本:filesize()でバイト数を取得する

<?php
$path = __DIR__ . '/sample.zip';
$bytes = filesize($path);          // 例)1234567
echo $bytes;                       // 生のバイト数

filesize() はローカルファイルのサイズをバイトで返します。返り値をそのまま出すのではなく、次の関数でわかりやすい単位へ変換してから表示します。

そのまま使える単位変換関数(1024 進・小数点桁数指定)

<?php
/**
 * バイト数を KB/MB/GB/TB に自動変換して文字列で返す(1024 進)
 *
 * @param int|float $bytes     バイト数
 * @param int       $decimals  小数点以下の桁数(例:1 → 1.2 MB)
 * @param bool      $trimZero  末尾の .0 を消すなら true
 * @return string              例)"1.2 MB", "984 B"
 */
function human_bytes($bytes, $decimals = 1, $trimZero = true)
{
    if (!is_numeric($bytes) || $bytes < 0) {
        return '0 B';
    }

    $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
    $factor = 0;

    // 1024 を底にして単位を上げる
    while ($bytes >= 1024 && $factor < count($units) - 1) {
        $bytes /= 1024;
        $factor++;
    }

    // 小数点の丸め
    $formatted = number_format($bytes, $decimals, '.', '');

    // 末尾 .0 を取りたい場合
    if ($trimZero && strpos($formatted, '.') !== false) {
        $formatted = rtrim(rtrim($formatted, '0'), '.');
    }

    return $formatted . ' ' . $units[$factor];
}

表示例:ダウンロードボタンの横にサイズを出す

<?php
$path = __DIR__ . '/sample.zip';
$size = file_exists($path) ? human_bytes(filesize($path), 1) : 'N/A';
?>

<a href="/download/sample.zip" class="btn">ダウンロード(<?= htmlspecialchars($size, ENT_QUOTES) ?>)</a>

小数点以下の桁数は第2引数で制御できます。デフォルトの 1 桁が一般的ですが、正確さを重視するなら 2 桁にしても構いません。

1000 進(SI)で表示したい場合

ストレージの表記ゆれを避けたいときは 1000 進(KB=1000B)で計算する選択肢もあります。差し替えるのは底だけです。

<?php
function human_bytes_si($bytes, $decimals = 1, $trimZero = true)
{
    if (!is_numeric($bytes) || $bytes < 0) return '0 B';
    $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
    $factor = 0;
    while ($bytes >= 1000 && $factor < count($units) - 1) {
        $bytes /= 1000;  // ここだけ 1000
        $factor++;
    }
    $formatted = number_format($bytes, $decimals, '.', '');
    if ($trimZero && strpos($formatted, '.') !== false) {
        $formatted = rtrim(rtrim($formatted, '0'), '.');
    }
    return $formatted . ' ' . $units[$factor];
}

ディレクトリの合計サイズを計算して表示する

ダウンロードページで「このフォルダ全体のサイズ」を出したいケースでは、再帰的に合算します。RecursiveDirectoryIteratorRecursiveIteratorIterator を使うと高速です。

<?php
function directory_size($dir)
{
    $total = 0;
    if (!is_dir($dir)) return 0;

    $it = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator(
            $dir,
            FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS
        )
    );

    foreach ($it as $file) {
        if ($file->isFile()) {
            $total += $file->getSize();
        }
    }
    return $total;
}

// 使い方
$dirBytes = directory_size(__DIR__ . '/assets');
echo human_bytes($dirBytes);   // 例)"243.6 MB"

大きなファイルでの注意点と実運用のコツ

64bit 環境の PHP では 2GB を超えるファイルでも filesize() が安定して扱えますが、32bit 環境では整数の上限で溢れることがあります。サーバーが 64bit であることを前提にするか、万一に備えエラー時は「N/A」などのフォールバック表示にしておくと堅牢です。リモートのファイルサイズを表示したい場合は、curlHEAD を投げて Content-Length を取得する方法もありますが、CDN や圧縮転送の影響を受けることがあるため、確実性は環境次第です。

まとめ:そのまま貼れる最小レシピ

<?php
$path = __DIR__ . '/sample.zip';

function human_bytes($bytes, $decimals = 1, $trimZero = true) {
    if (!is_numeric($bytes) || $bytes < 0) return '0 B';
    $u = ['B','KB','MB','GB','TB','PB']; $i = 0;
    while ($bytes >= 1024 && $i < count($u)-1) { $bytes /= 1024; $i++; }
    $s = number_format($bytes, $decimals, '.', '');
    if ($trimZero && strpos($s, '.') !== false) $s = rtrim(rtrim($s,'0'),'.');
    return $s . ' ' . $u[$i];
}

$sizeLabel = file_exists($path) ? human_bytes(filesize($path), 1) : 'N/A';
?>

<a href="/download/sample.zip">ダウンロード(<?= htmlspecialchars($sizeLabel, ENT_QUOTES) ?>)</a>

まずは上のレシピをサイトに組み込み、必要に応じて 1000 進への切り替えや桁数調整、ディレクトリ合計サイズの表示を加えると、ユーザーにとっても管理側にとっても扱いやすいサイズ表記が実現できます。