Načítání souborů

Školení, která pořádám

Dopsal jsem knihu

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).

Jakub Vrána, Dobře míněné rady, 21.3.2005, diskuse: 28 (nové: 0)

Diskuse

Dax:

Nechtelo by to vice rozepsat a uvest lepsi priklad?
# 21.3.2005 06:29:01 reagovat

ikona dgx:

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ší.
# 21.3.2005 11:11:13 reagovat

ikona Jakub Vrána:

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
?>
# 21.3.2005 12:05:13 reagovat

ikona dgx:

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ů"
# 21.3.2005 18:30:30 reagovat

ikona Jakub Vrána:

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.
# 21.3.2005 18:40:33 reagovat

ikona Jakub Vrána:

Netýká se to jen network streams. Viz http://bugs.php.net/bug.php?id=30936 a http://bugs.php.net/bug.php?id=35859.
# 9.1.2006 17:20:32 reagovat

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
# 21.3.2005 13:02:52 reagovat

ikona Jakub Vrána:

Pouhé oříznutí hlaviček obecně vzato bohužel nestačí kvůli různým Transfer-Encoding, hlavně chunked, které posílá např. samo PHP. Viz http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.41.
# 21.3.2005 15:04:35 reagovat

Mordae:

Pravda. Co ale poslat do requestu HTTP/1.0? To mi zajistí poměrně slušnou čitelnost. Zbytek se dá dochytat.
# 21.3.2005 20:20:23 reagovat

ikona 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).
# 22.3.2005 09:22:48 reagovat

ikona 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 ;)
# 21.3.2005 18:37:15 reagovat

ikona 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.
# 21.3.2005 18:47:26 reagovat

ikona 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

# 4.3.2008 01:32:47 reagovat

Mordae:

Pravda. Byl to ale jen nástin, stejně tuhle funkci napsalo určite už spoustu lidí...
# 21.3.2005 20:17:51 reagovat

ikona llook:

A nebo rovnou použít PHP Compat knihovnu - http://aidan.dotgeek.org/wiki/index.php?area=…=Installation
Ono těch funkcí v PHP < 4.3 chybí víc.
# 22.3.2005 10:48:04 reagovat

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.
# 23.3.2005 18:08:06 reagovat

ikona Jakub Vrána:

Polož dotaz třeba na http://diskuse.jakpsatweb.cz/index.php?act…&forum=9, tohle na to není nejvhodnější místo.
# 24.3.2005 09:45:14 reagovat

humanoidvani:

funguje krasne diky .... :-)
# 10.11.2005 00:51:16 reagovat

Samuraj:

thx :)
# 16.12.2005 20:09:09 reagovat

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.....
# 14.3.2006 17:44:29 reagovat

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
# 30.5.2006 03:36:42 reagovat

ikona Jakub Vrána:

Vždyť je to napsáno jen o kousíček dál: "context parameter can be skipped by NULL."
# 30.5.2006 12:13:36 reagovat

Tom@$:

heh, tak sem prave zistil, ze i kdyz je CZ verze psana EN, neni tam to sami jako v EN verzy :)
takze dik ;)
# 30.5.2006 14:49:44 reagovat

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;
    }
}
?>
# 17.3.2008 19:26:00 reagovat

ikona Jakub Vrána:

Proč se query string posílá po hlavičkách a ne přímo v GET požadavku?
# 18.3.2008 13:52:00 reagovat

ikona 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.
# 20.3.2008 13:13:19 reagovat

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?
# 9.5.2009 03:28:43 reagovat

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
# 30.6.2010 11:24:02 reagovat

Vložit příspěvek

Používejte diakritiku. Nelze používat HTML značky, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

© 2005-2010 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.