Мне нужно получить размер файла более 2 ГБ. (тестирование на 4,6 ГБ файла). Есть ли способ сделать это без внешней программы?
Текущее состояние:
filesize()
, stat()
а также fseek()
терпит неудачуfread()
а также feof()
работаетСуществует возможность получить размер файла путем чтения содержимого файла (очень медленно!).
$size = (float) 0;
$chunksize = 1024 * 1024;
while (!feof($fp)) {
fread($fp, $chunksize);
$size += (float) $chunksize;
}
return $size;
Я знаю, как получить его на 64-битных платформах (используя fseek($fp, 0, SEEK_END)
а также ftell()
), но мне нужно решение для 32-битной платформы.
Решение: Я начал проект с открытым исходным кодом для этого.
Big File Tools — это набор хаков, которые необходимы для манипулирования файлами размером более 2 ГБ в PHP (даже в 32-битных системах).
Вот один из возможных методов:
Сначала он пытается использовать подходящую для платформы команду оболочки (модификаторы подстановки оболочки Windows или * nix / Mac stat
команда). Если это не удается, он пытается COM (если на Windows), и, наконец, возвращается к filesize()
,
/*
* This software may be modified and distributed under the terms
* of the MIT license.
*/
function filesize64($file)
{
static $iswin;
if (!isset($iswin)) {
$iswin = (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN');
}
static $exec_works;
if (!isset($exec_works)) {
$exec_works = (function_exists('exec') && !ini_get('safe_mode') && @exec('echo EXEC') == 'EXEC');
}
// try a shell command
if ($exec_works) {
$cmd = ($iswin) ? "for %F in (\"$file\") do @echo %~zF" : "stat -c%s \"$file\"";
@exec($cmd, $output);
if (is_array($output) && ctype_digit($size = trim(implode("\n", $output)))) {
return $size;
}
}
// try the Windows COM interface
if ($iswin && class_exists("COM")) {
try {
$fsobj = new COM('Scripting.FileSystemObject');
$f = $fsobj->GetFile( realpath($file) );
$size = $f->Size;
} catch (Exception $e) {
$size = null;
}
if (ctype_digit($size)) {
return $size;
}
}
// if all else fails
return filesize($file);
}
Я начал проект под названием Инструменты для работы с большими файлами. это доказанный работать на Linux, Mac и Windows (даже 32-битных вариантах). Он обеспечивает точные байтовые результаты даже для больших файлов (> 4 ГБ). Внутренне он использует кирпич / математика — арифметическая библиотека произвольной точности.
Установите его, используя композитор.
composer install jkuchar/BigFileTools
и использовать это:
<?php
$file = BigFileTools\BigFileTools::createDefault()->getFile(__FILE__);
echo $file->getSize() . " bytes\n";
Result — BigInteger, поэтому вы можете вычислять результаты
$sizeInBytes = $file->getSize();
$sizeInMegabytes = $sizeInBytes->toBigDecimal()->dividedBy(1024*1024, 2, \Brick\Math\RoundingMode::HALF_DOWN);
echo "Size is $sizeInMegabytes megabytes\n";
Big File Tools внутренне использует драйверы для надежного определения точного размера файла на всех платформах. Вот список доступных драйверов (обновлено 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 | - | -
Вы можете использовать BigFileTools с любым из них или по умолчанию выбран самый быстрый из доступных (BigFileTools::createDefault()
)
use BigFileTools\BigFileTools;
use BigFileTools\Driver;
$bigFileTools = new BigFileTools(new Driver\CurlDriver());
<?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';
}
}
?>
Используйте как следовать:
<?php include("php/size.php"); ?>
И где вы хотите:
<?php showsize("files/VeryBigFile.rar"); ?>
Если вы хотите улучшить его, пожалуйста!
Я нашел хорошее тонкое решение для Linux / Unix только для получения файлов большого размера с 32-битным php.
$file = "/path/to/my/file.tar.gz";
$filesize = exec("stat -c %s ".$file);
Вы должны обращаться с $filesize
как строка Попытка приведения к типу int приводит к увеличению размера файла = PHP_INT_MAX, если размер файла больше, чем PHP_INT_MAX.
Но хотя он и обрабатывается как строка, работает следующий читаемый человеком алгоритм:
formatBytes($filesize);
public function formatBytes($size, $precision = 2) {
$base = log($size) / log(1024);
$suffixes = array('', 'k', 'M', 'G', 'T');
return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
}
поэтому мой вывод для файла размером более 4 Гб:
4.46G
$file_size=sprintf("%u",filesize($working_dir."\\".$file));
Это работает для меня на Windows Box.
Я просматривал журнал ошибок здесь: https://bugs.php.net/bug.php?id=63618 и нашел это решение.
Если у вас есть FTP-сервер, вы можете использовать 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);
(Найдено в качестве комментария к ftp_size страница)
вы можете захотеть добавить некоторые альтернативы к используемой вами функции, такие как вызов системных функций, таких как «dir» / «ls», и получить информацию оттуда. Конечно, они являются предметом безопасности, вещи, которые вы можете проверить и в конечном итоге вернуться к медленному методу только в качестве крайней меры.
Одним из вариантов будет поиск до отметки 2 ГБ, а затем прочитать длину оттуда …
function getTrueFileSize($filename) {
$size = filesize($filename);
if ($size === false) {
$fp = fopen($filename, 'r');
if (!$fp) {
return false;
}
$offset = PHP_INT_MAX - 1;
$size = (float) $offset;
if (!fseek($fp, $offset)) {
return false;
}
$chunksize = 8192;
while (!feof($fp)) {
$size += strlen(fread($fp, $chunksize));
}
} elseif ($size < 0) {
// Handle overflowed integer...
$size = sprintf("%u", $size);
}
return $size;
}
Так что, в основном, он ищет наибольшее целое число с положительным знаком, представимое в PHP (2 ГБ для 32-битной системы), а затем читает с использованием блоков по 8 КБ (что должно быть справедливым компромиссом для лучшей эффективности памяти по сравнению с эффективностью передачи диска).
Также обратите внимание, что я не добавляю $chunksize
по размеру. Причина в том, что fread
может на самом деле вернуть больше или меньше байтов, чем $chunksize
в зависимости от ряда возможностей. Так что вместо этого используйте strlen
определить длину анализируемой строки.