74
function foldersize($path) {
  $total_size = 0;
  $files = scandir($path);

  foreach($files as $t) {
    if (is_dir(rtrim($path, '/') . '/' . $t)) {
      if ($t<>"." && $t<>"..") {
          $size = foldersize(rtrim($path, '/') . '/' . $t);

          $total_size += $size;
      }
    } else {
      $size = filesize(rtrim($path, '/') . '/' . $t);
      $total_size += $size;
    }
  }
  return $total_size;
}

function format_size($size) {
  $mod = 1024;
  $units = explode(' ','B KB MB GB TB PB');
  for ($i = 0; $size > $mod; $i++) {
    $size /= $mod;
  }

  return round($size, 2) . ' ' . $units[$i];
}

$SIZE_LIMIT = 5368709120; // 5 GB

$sql="select * from users order by id";
$result=mysql_query($sql);

while($row=mysql_fetch_array($result)) {
  $disk_used = foldersize("C:/xampp/htdocs/freehosting/".$row['name']);

  $disk_remaining = $SIZE_LIMIT - $disk_used;
  print 'Name: ' . $row['name'] . '<br>';

  print 'diskspace used: ' . format_size($disk_used) . '<br>';
  print 'diskspace left: ' . format_size($disk_remaining) . '<br><hr>';
}

php disk_total_space

Any idea why the processor usage shoot up too high or 100% till the script execution is finish ? Can anything be done to optimize it? or is there any other alternative way to check folder and folders inside it size?

halfpastfour.am
  • 5,764
  • 3
  • 44
  • 61

20 Answers20

97
function GetDirectorySize($path){
    $bytestotal = 0;
    $path = realpath($path);
    if($path!==false && $path!='' && file_exists($path)){
        foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)) as $object){
            $bytestotal += $object->getSize();
        }
    }
    return $bytestotal;
}

The same idea as Janith Chinthana suggested. With a few fixes:

  • Converts $path to realpath
  • Performs iteration only if path is valid and folder exists
  • Skips . and .. files
  • Optimized for performance
Slava
  • 2,887
  • 3
  • 30
  • 39
  • 2
    both of these raised a Runtime exception when they came across broken symlinks for me. A simple try/catch around the `$bytestotal += $object->getSize();` resolved this for me. – Programster Aug 08 '14 at 22:08
  • 1
    To get KBs instead of Bytes, then divide the data by `1024` – T.Todua Oct 01 '14 at 14:19
  • if the folder is empty , the code returns an error Uncaught exception 'UnexpectedValueException' with message 'RecursiveDirectoryIterator:: – gr68 Mar 27 '17 at 08:15
  • 1
    Works for both Linux and Windows, folder doesn't exist returns 0 and if folder empty returns 0, not sure why @gr68 has issues, I think this should be the approved answer. – Joel Davis Apr 07 '17 at 00:29
  • 2
    Maybe gr68 meant when the $path variable is empty. Recently I saw `RecursiveDirectoryIterator` throwing an error on an empty path, so, just in case, I've adopted the code to check folder existence. – Slava Apr 07 '17 at 08:36
  • I've upvoted this post because it answers the question, but I think its claim to be optimized for performance is unfulfilled. – user10089632 Jan 16 '18 at 19:22
  • 2
    @user10089632 You are right from a certain point of view. It has no visible "optimizations". Maybe I should have left that claim out. But I wanted to let people know in some way that I have actually measured and compared the performance of tons of different syntaxes and approaches, and this exact code came out as fastest. – Slava Jan 17 '18 at 16:15
  • @Programster Ignoring exceptions isn't too elegant solution in my opinion... But you can change that `try {} catch()` block to a conditional statement where you check the current element if it's a (regular) file: `if ( $object->isFile() ) { $bytestotal += $object->getSize(); }` – Gergely Lukacsy Feb 15 '18 at 10:12
  • 2
    it still takes approx 81 seconds with a directory up to the size of 2.1 GB on windows as compared to using `$obj = new \COM('scripting.filesystemobject');` which does this in approx 1.8 secs – Muhammad Omer Aslam Oct 25 '18 at 01:24
  • Some directory may have problem and cause this error. **Uncaught RuntimeException: SplFileInfo::getSize()** Use `if ($object->isReadable())` before `getSize()` can help. – vee Jan 04 '21 at 09:08
  • Really useful, thank you! I added a divisor for MB and round param to shave off extra bytes. Much better than disk_total_space(). – Numabyte Aug 16 '22 at 04:34
47

The following are other solutions offered elsewhere:

If on a Windows Host:

<?
    $f = 'f:/www/docs';
    $obj = new COM ( 'scripting.filesystemobject' );
    if ( is_object ( $obj ) )
    {
        $ref = $obj->getfolder ( $f );
        echo 'Directory: ' . $f . ' => Size: ' . $ref->size;
        $obj = null;
    }
    else
    {
        echo 'can not create object';
    }
?>

Else, if on a Linux Host:

<?
    $f = './path/directory';
    $io = popen ( '/usr/bin/du -sk ' . $f, 'r' );
    $size = fgets ( $io, 4096);
    $size = substr ( $size, 0, strpos ( $size, "\t" ) );
    pclose ( $io );
    echo 'Directory: ' . $f . ' => Size: ' . $size;
?>
Álvaro González
  • 142,137
  • 41
  • 261
  • 360
Sampson
  • 265,109
  • 74
  • 539
  • 565
  • 4
    Note that the ' ' in strpos is a tab – Ashley Aug 29 '12 at 08:15
  • Source: http://forums.devshed.com/php-development-5/get-directory-size-with-php-efficiently-361124.html – Boldewyn Jan 10 '13 at 13:01
  • 1
    When I try this on a Windows host, I an error: `Fatal error: Class 'COM' not found`. – vertigoelectric May 10 '13 at 20:41
  • @vertigoelectric Make sure you've enabled the COM and .NET dll in your ini file. See comments on http://php.net/manual/en/book.com.php for additional details. – Sampson May 11 '13 at 20:49
  • Note that the Linux option can give you unwanted results, see [this question and the answers](http://unix.stackexchange.com/q/86985/60074). – halfpastfour.am Apr 23 '15 at 14:17
  • If you need to know the OS - `PHP_OS` constant - see https://stackoverflow.com/questions/5879043/php-script-detect-whether-running-under-linux-or-windows – jave.web Nov 29 '17 at 12:54
  • `popen` mostly disabled for security reasons unless we have vps Any alternative to that? – MR_AMDEV May 31 '19 at 00:51
  • 3
    Warning: if a hacker is able to choose the content of $f , this code is vulnerable to code execution. a hacker could make `$f` contain `./ & rm -rfv /` and delete everything. use escapeshellarg(): `$io = popen ( '/usr/bin/du -sk ' . escapeshellarg($f), 'r' );` instead. – hanshenrik Jul 28 '19 at 12:23
  • 1
    remember that you need to escape the spaces in the path or use `preg_quote($path,' ')` to escape the spaces in the path otherwise the LINUX solution wont work – Muhammad Omer Aslam Sep 30 '19 at 06:38
  • The size returned is in KB, not in B. – keanu_reeves Nov 23 '19 at 19:22
34

directory size using php filesize and RecursiveIteratorIterator.

This works with any platform which is having php 5 or higher version.

/**
 * Get the directory size
 * @param  string $directory
 * @return integer
 */
function dirSize($directory) {
    $size = 0;
    foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)) as $file){
        $size+=$file->getSize();
    }
    return $size;
} 
Kerem
  • 11,377
  • 5
  • 59
  • 58
Janith Chinthana
  • 3,792
  • 2
  • 27
  • 54
  • 9
    I found a bug in your code, this function add size of directory '..'. So, you have to limit it like this: `if($file -> getFileName() != '..') $size += $file -> getSize();` – Ajax Oct 16 '13 at 09:05
  • @Alph.Dev suggested an improved version below. Check it out. – Dzhuneyt Jul 15 '14 at 15:00
30

A pure php example.

<?php
    $units = explode(' ', 'B KB MB GB TB PB');
    $SIZE_LIMIT = 5368709120; // 5 GB
    $disk_used = foldersize("/webData/users/vdbuilder@yahoo.com");

    $disk_remaining = $SIZE_LIMIT - $disk_used;

    echo("<html><body>");
    echo('diskspace used: ' . format_size($disk_used) . '<br>');
    echo( 'diskspace left: ' . format_size($disk_remaining) . '<br><hr>');
    echo("</body></html>");


function foldersize($path) {
    $total_size = 0;
    $files = scandir($path);
    $cleanPath = rtrim($path, '/'). '/';

    foreach($files as $t) {
        if ($t<>"." && $t<>"..") {
            $currentFile = $cleanPath . $t;
            if (is_dir($currentFile)) {
                $size = foldersize($currentFile);
                $total_size += $size;
            }
            else {
                $size = filesize($currentFile);
                $total_size += $size;
            }
        }   
    }

    return $total_size;
}


function format_size($size) {
    global $units;

    $mod = 1024;

    for ($i = 0; $size > $mod; $i++) {
        $size /= $mod;
    }

    $endIndex = strpos($size, ".")+3;

    return substr( $size, 0, $endIndex).' '.$units[$i];
}

?>
Ry-
  • 218,210
  • 55
  • 464
  • 476
vdbuilder
  • 12,254
  • 2
  • 25
  • 29
  • 1
    vdbuilder & @minitech : What about the *processor usage* issue raised by the OP? – brasofilo Oct 31 '12 at 04:27
  • 2
    @brasofilo The op ended the question with 'or is there any other alternative way to check folder and folders inside it size?' This is an alternative way. If this code is used the processor usage problems with the op's original code are irrelevant. ;) – vdbuilder Oct 31 '12 at 15:01
  • Thanks for the feedback, vdbuilder. I just missed some textual explanation about why your code is an optimized version... As I used it to answer this [WordPress Question](http://wordpress.stackexchange.com/a/71054/12615) :o) – brasofilo Oct 31 '12 at 15:10
22
function get_dir_size($directory){
    $size = 0;
    $files = glob($directory.'/*');
    foreach($files as $path){
        is_file($path) && $size += filesize($path);
        is_dir($path)  && $size += get_dir_size($path);
    }
    return $size;
} 
Kerem
  • 11,377
  • 5
  • 59
  • 58
Alex Kashin
  • 575
  • 6
  • 13
16

Thanks to Jonathan Sampson, Adam Pierce and Janith Chinthana I did this one checking for most performant way to get the directory size. Should work on Windows and Linux Hosts.

static function getTotalSize($dir)
{
    $dir = rtrim(str_replace('\\', '/', $dir), '/');

    if (is_dir($dir) === true) {
        $totalSize = 0;
        $os        = strtoupper(substr(PHP_OS, 0, 3));
        // If on a Unix Host (Linux, Mac OS)
        if ($os !== 'WIN') {
            $io = popen('/usr/bin/du -sb ' . $dir, 'r');
            if ($io !== false) {
                $totalSize = intval(fgets($io, 80));
                pclose($io);
                return $totalSize;
            }
        }
        // If on a Windows Host (WIN32, WINNT, Windows)
        if ($os === 'WIN' && extension_loaded('com_dotnet')) {
            $obj = new \COM('scripting.filesystemobject');
            if (is_object($obj)) {
                $ref       = $obj->getfolder($dir);
                $totalSize = $ref->size;
                $obj       = null;
                return $totalSize;
            }
        }
        // If System calls did't work, use slower PHP 5
        $files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir));
        foreach ($files as $file) {
            $totalSize += $file->getSize();
        }
        return $totalSize;
    } else if (is_file($dir) === true) {
        return filesize($dir);
    }
}
André Fiedler
  • 1,093
  • 4
  • 16
  • 25
  • +1 for fastest multi-host solution But should the OS-Checks be like if ($os !== 'WIN' && $os !== 'Win') and elseif (extension_loaded('com_dotnet')) – Yannick Gottschalk Aug 10 '15 at 12:03
  • 1
    best optimized answer so far takes approx `1.3 secs` to calculate a dir around `2.16 GB` with approx `81,043 Files` and `16,713 Folders` as compared to recursive iterator which sleeps for about `80 secs` (ON WINDOWS) – Muhammad Omer Aslam Oct 25 '18 at 01:35
  • Works perfectly and efficient too .Should be below accepted :-) – MR_AMDEV Apr 13 '19 at 17:17
  • Thanks man for such a nice function but as `popen` is mostly disabled on most hostings for security reasons is there any alternative to that .The rest is just perfect. – MR_AMDEV May 31 '19 at 00:49
9

Even though there are already many many answers to this post, I feel I have to add another option for unix hosts that only returns the sum of all file sizes in the directory (recursively).

If you look at Jonathan's answer he uses the du command. This command will return the total directory size but the pure PHP solutions posted by others here will return the sum of all file sizes. Big difference!

What to look out for

When running du on a newly created directory, it may return 4K instead of 0. This may even get more confusing after having deleted files from the directory in question, having du reporting a total directory size that does not correspond to the sum of the sizes of the files within it. Why? The command du returns a report based on some file settings, as Hermann Ingjaldsson commented on this post.

The solution

To form a solution that behaves like some of the PHP-only scripts posted here, you can use ls command and pipe it to awk like this:

ls -ltrR /path/to/dir |awk '{print \$5}'|awk 'BEGIN{sum=0} {sum=sum+\$1} END {print sum}'

As a PHP function you could use something like this:

function getDirectorySize( $path )
{
    if( !is_dir( $path ) ) {
        return 0;
    }

    $path   = strval( $path );
    $io     = popen( "ls -ltrR {$path} |awk '{print \$5}'|awk 'BEGIN{sum=0} {sum=sum+\$1} END {print sum}'", 'r' );
    $size   = intval( fgets( $io, 80 ) );
    pclose( $io );

    return $size;
}
Community
  • 1
  • 1
halfpastfour.am
  • 5,764
  • 3
  • 44
  • 61
7

I found this approach to be shorter and more compatible. The Mac OS X version of "du" doesn't support the -b (or --bytes) option for some reason, so this sticks to the more-compatible -k option.

$file_directory = './directory/path';
$output = exec('du -sk ' . $file_directory);
$filesize = trim(str_replace($file_directory, '', $output)) * 1024;

Returns the $filesize in bytes.

Nate Lampton
  • 339
  • 2
  • 7
6

Johnathan Sampson's Linux example didn't work so good for me. Here's an improved version:

function getDirSize($path)
{
    $io = popen('/usr/bin/du -sb '.$path, 'r');
    $size = intval(fgets($io,80));
    pclose($io);
    return $size;
}
Adam Pierce
  • 33,531
  • 22
  • 69
  • 89
3

It works perfectly fine .

     public static function folderSize($dir)
     {
        $size = 0;

        foreach (glob(rtrim($dir, '/') . '/*', GLOB_NOSORT) as $each) {
            $func_name = __FUNCTION__;
            $size += is_file($each) ? filesize($each) : static::$func_name($each);
        }

        return $size;
      }
akshaypjoshi
  • 1,245
  • 1
  • 15
  • 24
3

if you are hosted on Linux:

passthru('du -h -s ' . $DIRECTORY_PATH)

It's better than foreach

farza
  • 31
  • 1
2

PHP get directory size (with FTP access)

After hard work, this code works great!!!! and I want to share with the community (by MundialSYS)

function dirFTPSize($ftpStream, $dir) {
    $size = 0;
    $files = ftp_nlist($ftpStream, $dir);

    foreach ($files as $remoteFile) {
        if(preg_match('/.*\/\.\.$/', $remoteFile) || preg_match('/.*\/\.$/', $remoteFile)){
            continue;
        }
        $sizeTemp = ftp_size($ftpStream, $remoteFile);
        if ($sizeTemp > 0) {
            $size += $sizeTemp;
        }elseif($sizeTemp == -1){//directorio
            $size += dirFTPSize($ftpStream, $remoteFile);
        }
    }

    return $size;
}

$hostname = '127.0.0.1'; // or 'ftp.domain.com'
$username = 'username';
$password = 'password';
$startdir = '/public_html'; // absolute path
$files = array();
$ftpStream = ftp_connect($hostname);
$login = ftp_login($ftpStream, $username, $password);
if (!$ftpStream) {
    echo 'Wrong server!';
    exit;
} else if (!$login) {
    echo 'Wrong username/password!';
    exit;
} else {
    $size = dirFTPSize($ftpStream, $startdir);
}

echo number_format(($size / 1024 / 1024), 2, '.', '') . ' MB';

ftp_close($ftpStream);

Good code! Fernando

Fernando
  • 1,126
  • 12
  • 13
2

Object Oriented Approach :

/**
 * Returns a directory size
 *
 * @param string $directory
 *
 * @return int $size directory size in bytes
 *
 */
function dir_size($directory)
{
    $size = 0;
    foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)) as $file)
    {
        $size += $file->getSize();
    }
    return $size;
}

Fast and Furious Approach :

function dir_size2($dir)
{
    $line = exec('du -sh ' . $dir);
    $line = trim(str_replace($dir, '', $line));
    return $line;
}
hugsbrugs
  • 3,501
  • 2
  • 29
  • 36
2

There are several things you could do to optimise the script - but maximum success would make it IO-bound rather than CPU-bound:

  1. Calculate rtrim($path, '/') outside the loop.
  2. make if ($t<>"." && $t<>"..") the outer test - it doesn't need to stat the path
  3. Calculate rtrim($path, '/') . '/' . $t once per loop - inside 2) and taking 1) into account.
  4. Calculate explode(' ','B KB MB GB TB PB'); once rather than each call?
Douglas Leeder
  • 52,368
  • 9
  • 94
  • 137
2

Code adjusted to access main directory and all sub folders within it. This would return the full directory size.

function get_dir_size($directory){
    $size = 0;
    $files= glob($directory.'/*');
    foreach($files as $path){
        is_file($path) && $size += filesize($path);

        if (is_dir($path))
        {
            $size += get_dir_size($path);
        }
    }
    return $size;
}
Ifeanyi Amadi
  • 776
  • 5
  • 10
1

Regarding Johnathan Sampson's Linux example, watch out when you are doing an intval on the outcome of the "du" function, if the size is >2GB, it will keep showing 2GB.

Replace:

$totalSize = intval(fgets($io, 80));

by:

strtok(fgets($io, 80), " ");

supposed your "du" function returns the size separated with space followed by the directory/file name.

M J
  • 11
  • 1
1

Just another function using native php functions.

function dirSize($dir)
    {
        $dirSize = 0;
        if(!is_dir($dir)){return false;};
        $files = scandir($dir);if(!$files){return false;}
        $files = array_diff($files, array('.','..'));

        foreach ($files as $file) {
            if(is_dir("$dir/$file")){
                 $dirSize += dirSize("$dir/$file");
            }else{
                $dirSize += filesize("$dir/$file");
            }
        }
        return $dirSize;
    }

NOTE: this function returns the files sizes, NOT the size on disk

Community
  • 1
  • 1
Accountant م
  • 6,975
  • 3
  • 41
  • 61
0

Evolved from Nate Haugs answer I created a short function for my project:

function uf_getDirSize($dir, $unit = 'm')
{
    $dir = trim($dir, '/');
    if (!is_dir($dir)) {
        trigger_error("{$dir} not a folder/dir/path.", E_USER_WARNING);
        return false;
    }
    if (!function_exists('exec')) {
        trigger_error('The function exec() is not available.', E_USER_WARNING);
        return false;
    }
    $output = exec('du -sb ' . $dir);
    $filesize = (int) trim(str_replace($dir, '', $output));
    switch ($unit) {
        case 'g': $filesize = number_format($filesize / 1073741824, 3); break;  // giga
        case 'm': $filesize = number_format($filesize / 1048576, 1);    break;  // mega
        case 'k': $filesize = number_format($filesize / 1024, 0);       break;  // kilo
        case 'b': $filesize = number_format($filesize, 0);              break;  // byte
    }
    return ($filesize + 0);
}
maxpower9000
  • 223
  • 2
  • 8
0

A one-liner solution. Result in bytes.

$size=array_sum(array_map('filesize', glob("{$dir}/*.*")));

Added bonus: you can simply change the file mask to whatever you like, and count only certain files (eg by extension).

Steve Horvath
  • 508
  • 1
  • 4
  • 10
  • warning, this solution fails on files not having a period in their name (like extension-less files): ```$ mkdir test $ echo test >test/test_file_without_extension $ php -r '$size=array_sum(array_map("filesize", glob("test/*.*")));var_dump($size);'```: int(0) – hanshenrik Jul 28 '19 at 12:31
  • @hanshenrik, you can simply change the file mask for your case - or other cases as well. – Steve Horvath Aug 09 '19 at 03:02
0

This is the simplest possible algorithm to find out directory size irrespective of the programming language you are using. For PHP specific implementation. go to: Calculate Directory Size in PHP | Explained with Algorithm | Working Code

Ayush Jain
  • 337
  • 3
  • 10