PHP x86 How to get filesize of > 2 GB file without

2019-01-04 01:34发布

I need to get the file size of a file over 2 GB in size. (testing on 4.6 GB file). Is there any way to do this without an external program?

Current status:

  • filesize(), stat() and fseek() fails
  • fread() and feof() works

There is a possibility to get the file size by reading the file content (extremely slow!).

$size = (float) 0;
$chunksize = 1024 * 1024;
while (!feof($fp)) {
    fread($fp, $chunksize);
    $size += (float) $chunksize;
}
return $size;

I know how to get it on 64-bit platforms (using fseek($fp, 0, SEEK_END) and ftell()), but I need solution for 32-bit platform.


Solution: I've started open-source project for this.

Big File Tools

Big File Tools is a collection of hacks that are needed to manipulate files over 2 GB in PHP (even on 32-bit systems).

14条回答
男人必须洒脱
2楼-- · 2019-01-04 01:59

If you have an FTP server you could use fsockopen:

$socket = fsockopen($hostName, 21);
$t = fgets($socket, 128);
fwrite($socket, "USER $myLogin\r\n");
$t = fgets($socket, 128);
fwrite($socket, "PASS $myPass\r\n");
$t = fgets($socket, 128);
fwrite($socket, "SIZE $fileName\r\n");
$t = fgets($socket, 128);
$fileSize=floatval(str_replace("213 ","",$t));
echo $fileSize;
fwrite($socket, "QUIT\r\n");
fclose($socket); 

(Found as a comment on the ftp_size page)

查看更多
叼着烟拽天下
3楼-- · 2019-01-04 02:00

I've started project called Big File Tools. It is proven to work on Linux, Mac and Windows (even 32-bit variants). It provides byte-precise results even for huge files (>4GB). Internally it uses brick/math - arbitrary-precision arithmetic library.

Install it using composer.

composer install jkuchar/BigFileTools

and use it:

<?php
$file = BigFileTools\BigFileTools::createDefault()->getFile(__FILE__);
echo $file->getSize() . " bytes\n";

Result is BigInteger so you can compute with results

$sizeInBytes = $file->getSize();
$sizeInMegabytes = $sizeInBytes->toBigDecimal()->dividedBy(1024*1024, 2, \Brick\Math\RoundingMode::HALF_DOWN);    
echo "Size is $sizeInMegabytes megabytes\n";

Big File Tools internally uses drivers to reliably determine exact file size on all platforms. Here is list of available drivers (updated 2016-02-05)

| Driver           | Time (s) ↓          | Runtime requirements | Platform 
| ---------------  | ------------------- | --------------       | ---------
| CurlDriver       | 0.00045299530029297 | CURL extension       | -
| NativeSeekDriver | 0.00052094459533691 | -                    | -
| ComDriver        | 0.0031449794769287  | COM+.NET extension   | Windows only
| ExecDriver       | 0.042937040328979   | exec() enabled       | Windows, Linux, OS X
| NativeRead       | 2.7670161724091     | -                    | -

You can use BigFileTools with any of these or fastest available is chosen by default (BigFileTools::createDefault())

 use BigFileTools\BigFileTools;
 use BigFileTools\Driver;
 $bigFileTools = new BigFileTools(new Driver\CurlDriver());
查看更多
贪生不怕死
4楼-- · 2019-01-04 02:00
<?php
  ######################################################################
  # Human size for files smaller or bigger than 2 GB on 32 bit Systems #
  # size.php - 1.1 - 17.01.2012 - Alessandro Marinuzzi - www.alecos.it #
  ######################################################################
  function showsize($file) {
    if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
      if (class_exists("COM")) {
        $fsobj = new COM('Scripting.FileSystemObject');
        $f = $fsobj->GetFile(realpath($file));
        $file = $f->Size;
      } else {
        $file = trim(exec("for %F in (\"" . $file . "\") do @echo %~zF"));
      }
    } elseif (PHP_OS == 'Darwin') {
      $file = trim(shell_exec("stat -f %z " . escapeshellarg($file)));
    } elseif ((PHP_OS == 'Linux') || (PHP_OS == 'FreeBSD') || (PHP_OS == 'Unix') || (PHP_OS == 'SunOS')) {
      $file = trim(shell_exec("stat -c%s " . escapeshellarg($file)));
    } else {
      $file = filesize($file);
    }
    if ($file < 1024) {
      echo $file . ' Byte';
    } elseif ($file < 1048576) {
      echo round($file / 1024, 2) . ' KB';
    } elseif ($file < 1073741824) {
      echo round($file / 1048576, 2) . ' MB';
    } elseif ($file < 1099511627776) {
      echo round($file / 1073741824, 2) . ' GB';
    } elseif ($file < 1125899906842624) {
      echo round($file / 1099511627776, 2) . ' TB';
    } elseif ($file < 1152921504606846976) {
      echo round($file / 1125899906842624, 2) . ' PB';
    } elseif ($file < 1180591620717411303424) {
      echo round($file / 1152921504606846976, 2) . ' EB';
    } elseif ($file < 1208925819614629174706176) {
      echo round($file / 1180591620717411303424, 2) . ' ZB';
    } else {
      echo round($file / 1208925819614629174706176, 2) . ' YB';
    }
  }
?>

Use as follow:

<?php include("php/size.php"); ?>

And where you want:

<?php showsize("files/VeryBigFile.rar"); ?>

If you want improve it you are welcome!

查看更多
Fickle 薄情
5楼-- · 2019-01-04 02:05
$file_size=sprintf("%u",filesize($working_dir."\\".$file));

This works for me on a Windows Box.

I was looking through the bug log here: https://bugs.php.net/bug.php?id=63618 and found this solution.

查看更多
做自己的国王
6楼-- · 2019-01-04 02:06

Well easyest way to do that would be to simply add a max value to your number. This means on x86 platform long number add 2^32:

if($size < 0) $size = pow(2,32) + $size;

example: Big_File.exe - 3,30Gb (3.554.287.616 b) your function returns -740679680 so you add 2^32 (4294967296) and get 3554287616.

You get negative number because your system reserves one bit of memory to the negative sign, so you are left with 2^31 (2.147.483.648 = 2G) maximum value of either negative or positive number. When system reaches this maximum value it doesn't stop but simply overwrites that last reserved bit and your number is now forced to negative. In simpler words, when you exceed maximum positive number you will be forced to maximum negative number, so 2147483648 + 1 = -2147483648. Further addition goes towards zero and again towards maximum number.

As you can see it is like a circle with highest and lowest numbers closing the loop.

Total maximum number that x86 architecture can "digest" in one tick is 2^32 = 4294967296 = 4G, so as long as your number is lower than that, this simple trick will always work. In higher numbers you must know how many times you have passed the looping point and simply multiply it by 2^32 and add it to your result:

$size = pow(2,32) * $loops_count + $size;

Ofcourse in basic PHP functions this is quite hard to do, because no function will tell you how many times it has passed the looping point, so this won't work for files over 4Gigs.

查看更多
该账号已被封号
7楼-- · 2019-01-04 02:07

you may want to add some alternatives to the function you use such as calling system functions such as "dir" / "ls" and get the information from there. They are subject of security of course, things you can check and eventually revert to the slow method as a last resort only.

查看更多
登录 后发表回答