Načítání souborů
Školení, která pořádám
Viděl jsem už spoustu způsobů načítání souborů.
<?php
// špatný způsob
$fp = fopen($filename, "r");
$file = fread($fp, filesize($filename));
fclose($fp);
// neefektivní způsob
$file = implode("", file($filename));
// správný způsob
$file = file_get_contents($filename);
?>
Funkce file_get_contents byla přidána sice až v PHP 4.3.0 (prosinec 2002), ale kdo by už kvůli bezpečnostním dírám používal starší verze, že. První způsob je špatně proto, že druhý parametr funkce fread znamená počet bytů, které se mohou maximálně načíst (a ne počet bytů, které se bezpodmínečně musí načíst), a navíc funkci filesize nelze použít u vzdálených souborů (dostupných např. protokolem HTTP).
Diskuse
Dax:
Nechtelo by to vice rozepsat a uvest lepsi priklad?
no Jakube, s tím počtem bytů, které se mohou maximálně načíst jsi trošku přestřelil, ne? :-)
Samozřejmě jde o počet bytů, které se načtou, pokud náhodou soubor není menší.
Je to opravdu ještě rozšířenější omyl než jsem čekal, když si to myslíš i ty. V dokumentaci je přitom jasně napsáno "reads up to $length bytes".
U lokálních souborů se obvykle načte celá délka, ale funkce to nezaručuje, což se projeví např. u vzdálených souborů:
<?php
$filename = "http://www.vrana.cz/";
$fp = fopen($filename, "rb");
echo strlen(fread($fp, 10000)) . "\n"; // 1460
echo strlen(file_get_contents($filename)) . "\n"; // 2818
?>
mrkni ba dokumentaci: fread() reads up to length bytes from the file ... Reading stops when length bytes have been read, EOF (end of file) is reached, or (for network streams) when a packet becomes available, whichever comes first.
Tedy u lokálních souborů se přečtě vše, až do EOF. Výjimka jsou network streams, ale to je trošku mimo téma "Načítání souborů"
Nikde netvrdím, že pro lokální soubory to nemůže fungovat. Ale obecně se na to spolehnout nedá. Když si na to člověk zvykne, tak se pak může divit, proč mu nefunguje takovýto kód:
<?php
$fp = fsockopen("www.vrana.cz", 80);
// poslání a přečtení hlaviček, mimo jiné i Content-Length
$data = fread($fp, $length);
?>
Několikrát jsem se s tím setkal, jednou jsem se do této pasti i sám lapil. Proto je lepší vědět, že druhý parametr funkce fread nezaručuje přečtení daného počtu bajtů, na což jsem chtěl mimo jiné upozornit.
Mordae:
Taky načítání touhle metodou je na dvě věci, moje oblíbené je:
<?php
$fp = fopen($filename, "r");
$file = '';
while(!feof($fp)){
$file .= fread($fp, 1024);
}
fclose($fp);
?>
A na soubory pod 10kB se vyplatí i implode('', file($filename)). O ty milisekundy strach nemám.
A co se týká file_get_contents(), což je opravdu správný způsob, php > 4.3.0 se nemusí vyskytovat všude. Natož doma v testovací instalaci. Nevím jak kdy, ale já updatuju méně často než na mě YaST huláká že už to potřebuji...
A protože by toto mělo být konstruktivní, doporučuji toto:
<?php
if(!function_exists('file_get_contents')){
function file_get_contents($filename, $use_include_path = 0){
$fp = fopen($filename, "r", $use_include_path);
$file = '';
while(!feof($fp)){
$file .= fread($fp, 1024);
}
fclose($fp);
return $file;
}
}
?>
Na maxlength atd. se můžete vykašlat - stejně jsou jen v php 5.1. Také by bylo možné vylepšit přeposílání chyb.
Také doporučuji místo url wraperů používat fsockopen a oříznout ručně hlavičky (první výskyt \n\n nebo \r\n\r\n). Funguje to i když někdo fopen _wrappers vypne.
---
Za případné chyby se předem omlouvám, netestoval jsem konkrétně tyto případy - píšu ze školy.
mordae@mordae.net
Mordae:
Pravda. Co ale poslat do requestu HTTP/1.0? To mi zajistí poměrně slušnou čitelnost. Zbytek se dá dochytat.
Jakub Vrána :
To je možné pokud vím pouze u serverů, které nepoužívají Virtual Hosty (protože jinak se musí poslat hlavička Host, která tuším patří až do HTTP/1.1).
spaze:
Taky mam pocit, ze url-wrappers jsou doporucovany (abstraktni) zpusob. Kdyz nekdo vypne url-wrappers, nejspis vypne i fsockopen, curl apod.
A k tomu if (!function_exists..), asi bych spis pouzil http://pear.php.net/PHP_Compat, implementace je sice stejna, ale uz je to hotovy ;)
Jakub Vrána :
URL wrappers může webmaster vypnout jako ochranu před ignoranty, kteří bez jakéhokoliv ověření používají kód <?php include $_GET["file"]; ?>. Když totiž někdo do parametru file předá plné URL, tak se kód na tomto URL provede v kontextu prováděného skriptu. Už na to téma mám něco napsané.
Zmiňuji to proto, že vypnutí URL wrappers nemusí nutně znamenat také vypnutí fsockopen a podobných.
spaze:
Dneska (od 5.2.0) na to mame samostatnou direktivu:
; Whether to allow include/require to open URLs (like http:// or ftp://) as files.
allow_url_include = Off
Mordae:
Pravda. Byl to ale jen nástin, stejně tuhle funkci napsalo určite už spoustu lidí...
Mike:
Neni to prilis k tematu, ale mam otazku.
Mam soubor napriklad pokus.htm, ma hlavicku, obsahuje tagy <body>, <html> atd.
Potrebuji skript, ktery otevre zadany html soubor, nahradi vsechen text, ktery se nenachazi v tazich, urcitym retezcem. Tzn, ze skript napriklad nenahradi "<meta http-equiv>" za "<nahrazovaci_retezec>", ale treba toto uz nahradi:
<body>
Nejaky text v souboru pokus.htm
</body>
Nahradi za:
<body>
Nahrazovaci_retez
</body>
V regulernich vyrazech nejsem vubec zbehly, takze se na to ptam.
humanoidvani:
funguje krasne diky .... :-)
Michal:
Hmmm....tak som vyskusal nacitat textovy subor funkciou file_get_contents(); ale je to omnoho pomalejsie ako ked "klasicky" citam riadok po riadku fnc FgetS(); a kontrolujem koniec suboru metodou napr. while (!Feof($fp));
Nechapem to.....
Tom@$:
potrebuju postupne nacist soubor (vypisuju nekolika MB) ale moc sem nepochopil co mam dat za "resource context"
vychazim z (uvedene na php.net)
string file_get_contents ( string filename [, bool use_include_path [, resource context [, int offset [, int maxlen]]]] )
takze abych mohl pouzit "offset" a "maxlen" k postupnemu nacitani souboru, musim uvezt i "resource context". co tam dat???
diky moc
Jakub Vrána :
Vždyť je to napsáno jen o kousíček dál: "context parameter can be skipped by NULL."
Tom@$:
heh, tak sem prave zistil, ze i kdyz je CZ verze psana EN, neni tam to sami jako v EN verzy :)
takze dik ;)
Jakub Kříž:
Používám tohle pro plnou kontrolu. Nektere servery me s file_get_contents nepustily.
<?php
function file_get_sock($url,$headers=false) {
$url = parse_url($url);
if (!isset($url['port'])) {
if ($url['scheme'] == 'http') { $url['port']=80; }
elseif ($url['scheme'] == 'https') { $url['port']=443; }
}
$url['query']=isset($url['query'])?$url['query']:'';
$url['protocol']=$url['scheme'].'://';
$eol="\r\n";
$headers = "GET ".$url['protocol'].$url['host'].$url['path']." HTTP/1.0".$eol.
"Host: ".$url['host'].$eol.
"Referer: ".$url['protocol'].$url['host'].$url['path'].$eol.
"Content-Type: application/x-www-form-urlencoded".$eol.
"Content-Length: ".strlen($url['query']).$eol.
$eol.$url['query'];
$fp = fsockopen($url['host'], $url['port'], $errno, $errstr, 30);
if($fp) {
fputs($fp, $headers);
$result = '';
while(!feof($fp)) { $result .= fgets($fp, 128); }
fclose($fp);
if (!$headers) {
$pattern="/^.*\r\n\r\n/s";
$result=preg_replace($pattern,'',$result);
}
return $result;
}
}
?>
Jakub Vrána :
Proč se query string posílá po hlavičkách a ne přímo v GET požadavku?
Jakub Vrána :
Hlavička Host je k dispozici až v HTTP/1.1, správně bys tedy měl komunikovat tímto protokolem. Pak se ovšem musíš vypořádat také např. s hlavičkou Transfer-Encoding: chunked.
petr:
Jak je to s legálností?Využití cizího serveru pro vlastní potřeby?Žádný obsah nenačtu do svého webu jen tím vykonám formulář na jiném serveru, je to v pořádku?
JusepeG:
Dobrý den,
snažím se pomocí kódu:
header("Cache-Control:no-cache");
$url= "
http://www.bezvakup.cz/rss/seznam.xml";
$xml= file_get_contents($url);
if(!empty($xml))echo($xml);
z jednoho serveru načíst soubor z jiného serveru.
Před měsícem mi to fungovalo, bohužel nyní mi to píše:
Warning: file_get_contents(
http://www.bezvakup.cz/rss/seznam.xml) [function.file-get-contents]: failed to open stream: Při systémovém volání, při kterém nikdy nemělo dojít k chybě, došlo k chybě. in C:\wamp\www\reklama\reklama.php on line 4
Nevím si rady, kde může být problém?
Děkuji
JusepeG
piiiiiiiiiip:
Ahoj nan soubor neco.php?vypis=clanky a chci aby se mi obsach zobrazil v souboru neco.php.Jak na to?
Dominik David:
Zdravím, mám problém částečně navazující na příspěvek.
Potřebuji pomocí PHP načíst a zpracovat tento TXT soubor s daty:
http://www.ceps.cz/_layouts/Ceps/_Pages/GraphData.…&ver=RT&
Bohužel při použití file_get_contents se dostanu na HTML stránku s errorem:
Error
Object reference not set to an instance of an object. at Ceps.Moss.Web.Controls.Moduls.PreventOfficeDiscoveryModule.context_PostAuthenticateRequest(Object sender, EventArgs e)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Díky za pomoc.
Jonez1:
Zdravim,
problem je ten, ze stranka ocekava nejake hlavicky pozadavku, ktere ji URL wrapper nejspise neposkytuje - odchazi jen velice omezeny pocet.
Lze to obejit tim, ze funkci file_get_contents hlavicky vnutis nasledujicim zpusobem
(
viz PHP - manual k funkci
http://www.php.net/manual/en/function.file-…#example-2303)
<?php
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: cs\r\n" .
"User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; cs; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14\r\n"
)
);
$context = stream_context_create($opts);
$url= "http://www.ceps.cz/_layouts/Ceps/_Pages/GraphData.…&ver=RT&";
// otevri soubor s pouzitim hlavicek
$fc= file_get_contents($url, false, $context);
// kontrola hlavicek odezvy + vystupu
echo "<pre>". print_r($http_response_header, 1). "</pre>";
echo "<pre>". print_r($fc, 1). "</pre>";
?>
Michal:
Zajímalo by mě, zda je možné po načtení souboru pomocí file_get_contents výsledný řetězec dále zpracovat. Potřeboval bych jednotlivé řádky do pole. Bohužel, explode mi problém nevyřeší (si myslím).
Pavel Beran:
taky si mohl napsat jak, to miluju takovýdle debily
Wautip:
A ono je to přitom někdy tak jednoduchý
Diskuse je zrušena z důvodu spamu.