Zjištění velikosti obrovského souboru
- Honza Kuchař
- Člen | 1662
Ahojte,
narazil jsem asi na omezení PHPka a nevím zatím jak na to. Chtěl jsem přes
FileDownloader odeslat soubor větší než 2 GB a nastaly problémy. Zdá se,
že PHP prostě nějak normálně neumí zjistit velikost souboru nad 2 GB.
Máte nějaký spolehlivý způsob, jak získat velikost souboru jakékoli velikosti?
Děkuji moc!
A výsledkem této diskuse jsou BigFileTools.
Editoval Honza Kuchař (5. 4. 2011 19:21)
- voda
- Člen | 561
V dokumentaci se píše:
Note: Because PHP's integer type is signed and many platforms use 32bit integers, filesize() may return unexpected results for files which are larger than 2GB. For files between 2GB and 4GB in size this can usually be overcome by using sprintf(„%u“, filesize($file)).
- jtousek
- Člen | 951
Citace z PHP manuálu.
Note: Because PHP's integer type is signed and many platforms use 32bit integers, filesize() may return unexpected results for files which are larger than 2GB. For files between 2GB and 4GB in size this can usually be overcome by using
sprintf("%u", filesize($file))
.
Jinak na 64bit platformách PHP myslím používá 64bit integer, takže tam by asi problém nebyl.
Další rady najdeš v komentářích uživatelů u dokumentace filesize.
EDIT: Koukám že voda byl rychlejší. :)
Editoval jtousek (30. 3. 2011 23:15)
- Honza Kuchař
- Člen | 1662
Tam jsem samozrejme patral jako prvni. Ale po nekolika denim patrani po internetu porad nic, co by bylo cross platform… A nepouzivalo by to exec. Toto treba vypada velmi dobre, ale si to funguje jen na linuxu, protoze na windows to cte ty obrovske soubory po bajtu a potom z toho vylezou petabajty. :( (http://www.php.net/…filesize.php#…)
Tedy do 8GB vypada spolehlive toto:
http://www.php.net/…filesize.php#…
Ale jak dale? Obcas potrebuji takto stahnout i soubory kolem 20GB.
A ftell() take umre pri techto velikostech.
- Honza Kuchař
- Člen | 1662
Foowie: jojo, to je sice fajn, ale to vidim fakt az jako posledni mozne reseni, pokud to fakt jinak nejde.
- Honza Kuchař
- Člen | 1662
Ani 64bitů to prý neřeší. filesize() nefunguje pořád. Ale alspoň funguje ftell().
Ještě k tomu kódu, vzhlem k tomu, že se jedná o doplněk, nerad bych tam volal tyto metody, protože asi byste docela neradi viděli, že by třeba Nette bylo závislé na povoleném execu.
- Honza Kuchař
- Člen | 1662
Tak zatim to vypada nasledovne:
- filesize
- unsigned filesize
- exec
- fread do konce souboru
Vracime float.
Jeste nejake napady jak to udelat?
- Milo
- Nette Core | 1283
Můj nápad. Chtělo by to ale pořádně ošetřit a otestovat.
function fixSeek($fd, $plusOffset)
{
$div = floor($plusOffset / PHP_INT_MAX);
$rem = $plusOffset % PHP_INT_MAX;
for($i = 0; $i < $div; $i++)
{
fseek($fd, PHP_INT_MAX, SEEK_CUR);
}
fseek($fd, $rem, SEEK_CUR);
}
function fixFilesize($fileName)
{
$seekSize = 2.0 * (PHP_INT_MAX + 1);
$fd = fopen($fileName, 'r');
$fileSize = filesize($fileName);
if( $fileSize > 0 )
{
fixSeek($fd, $fileSize);
}
while( fgetc($fd) !== false ) // TODO: anti loop?
{
$fileSize += $seekSize;
fixSeek($fd, $seekSize - 1); // Minus fgetc() char
}
fclose($fd);
return $fileSize;
}
echo fixFilesize('/home/milo/Avatar.mkv'); // 16017725424
EDIT:
Mám zpětnou vazu, že to nefunguje na WinXP, což jsem i ověřil. fseek()
funguje podivně.
Zkusil jsem ale variantu s cUrl a funguje mi na Debianu i WinXP. Cesta k souboru musí
být jako stream file:///
Editoval Milo (1. 4. 2011 14:08)
- Honza Kuchař
- Člen | 1662
Ano, je to tak, toto už jsem také zkoušel. ;) Na Linuxu to funguje, na Windows nikoliv. (ukazuje to nějaké terabajty, na Windows se to chová opravdu nedefinovaně)
Už mám takový nástřel, pak vám to sem dám. Zatím to funguje perfektně. ;)
- kravčo
- Člen | 721
Trochu som to skúšal a dospel som k tomu, že fseek()
na
Windowse zlyhá pri posune na pozíciu väčšiu ako PHP_MAX_INT
(0x7fffffff
pre 32bit).
<?php
$fp = fopen(__DIR__ . '/files/8gb.file', 'rb');
echo fseek($fp, 0x0000ffff, SEEK_SET), "\n"; // 0
echo "\n";
echo fseek($fp, 0x7fffffff, SEEK_SET), "\n"; // 0
echo "\n";
echo fseek($fp, 0x80000000, SEEK_SET), "\n"; // -1
echo "\n";
echo fseek($fp, 0xffffffff, SEEK_SET), "\n"; // -1
echo "\n";
echo fseek($fp, 0x3fffffff, SEEK_SET), "\n"; // 0
echo fseek($fp, 0x3fffffff, SEEK_CUR), "\n"; // 0
echo fseek($fp, 0x3fffffff, SEEK_CUR), "\n"; // -1
WIN PHP 5.3.5nts
- Honza Kuchař
- Člen | 1662
Tak nástřel se nachází v distribuci FileDownloaderu, který nyní podporuje stahování souborů větších než 2GB. Zatím však nebude fungovat segmenové stahování na segmetech se „startovní“ pozicí nad 2GB.
Už mám v hlavě řešení, ale bohužel, není to zrovna dvakrát
efektivní. Jediné co totiž funguje na posouvání kurzoru je
fread()
. Ten funguje i na místech mimo PHP_INT_MAX. Netušíte
tedy o nějakém efektivním způsobu, jak se přesunout někam dále (bez
čtení všech dat v souboru, resp. (velikost dat – PHP_INT_MAX))?
Případně neexistuje nějaká knihovna na práci s velkými soubory? (třeba i PHP extension)
PS: Co zkusit cUrl? :-) Ten vypadá velmi dobře, co se týče podpory
velikých souborů…
Nástřel použití curlu, na čtení obrovského souboru, s počátkem
čtením nad PHP_INT_MAX
<?php
$writefn = function($ch, $chunk) {
static $data='';
static $limit = 500; // 500 bytes, it's only a test
$len = strlen($data) + strlen($chunk);
if ($len >= $limit ) {
$data .= substr($chunk, 0, $limit-strlen($data));
echo $data;
return -1;
}
$data .= $chunk;
return strlen($chunk);
};
$path = "trailer1.avi";
$start = PHP_INT_MAX+222112112; // float
$ch = curl_init("file://" . realpath($path));
$range = $start.'-'.($start+500);
curl_setopt($ch, CURLOPT_RANGE, $range);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $writefn);
$curlRet = curl_exec($ch);
if($curlRet === false) {
echo "cUrl error: ".curl_error($ch);
}
Případně asi jdou nějak číst soubory z bashe, ne? Nemáte nějaké tipy, jak na Windows a jak na Linux?
- Honza Kuchař
- Člen | 1662
Implementováno ve File Downloaderu. A otestováno že MFU s pluploadem nemá problém s velkými soubory… (>4GB)
- Milo
- Nette Core | 1283
Trochu jsem testoval. Na WinXP 32bit, PHP 5.2.6, Apache 2, PHP jako modul vrací správné velikosti pro soubory o velikosti
PHP_INT_MAX nativeSeek, cUrl, sizeExec, sizeCom, sizeNativeRead
PHP_INT_MAX + 1 nativeSeek, cUrl, sizeExec, sizeCom, sizeNativeRead
PHP_INT_MAX + PHP_INT_MAX + 2 cUrl, sizeExec, sizeCom, sizeNativeRead
PHP_INT_MAX + PHP_INT_MAX + 3 cUrl, sizeExec, sizeCom, sizeNativeRead
16017725424 (cca. 16GB) cUrl, sizeExec, sizeCom, sizeNativeRead
kde PHP_INT_MAX je 2147483647, tj. 0x7fffffff
A subjektivně mi přijde, že sizeCom je několikrát rychlejší než sizeExec.
Jen pro soubor s nulovou velikostí vyhodí InvalidStateException.
- Honza Kuchař
- Člen | 1662
Také si myslím, že to tak je. PS: Když jsi to testoval nechceš
přidat volání Debug::timer();, který ti vrátí, jak dlouho jednotlivá
volání trvala? Udělám já.
- Honza Kuchař
- Člen | 1662
Výsledek profilování (seřazeno od nejrychlejšího v sekundách)
sizeCurl 0.00045299530029297
sizeNativeSeek 0.00052094459533691
sizeCom 0.0031449794769287
sizeExec 0.042937040328979
sizeNativeRead 2.7670161724091