Načítání souborů

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, on-line

Diskuse

Dax:

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

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

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

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

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

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

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

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

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

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

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

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

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

Mordae:

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

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

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

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

humanoidvani:

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

Samuraj:

thx :)
16.12.2005 20:09:09

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

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 04:36:42

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 13:13:36

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 15:49:44

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

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

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

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 04:28:43

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 12:24:02

piiiiiiiiiip:

Ahoj nan soubor neco.php?vypis=clanky a chci aby se mi obsach zobrazil v souboru neco.php.Jak na to?
29.8.2010 22:41:21

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.
6.4.2012 17:49:47

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>";

?>

19.12.2012 15:27:46

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).
18.9.2012 19:25:51

Michal:

Vyřešeno.
19.9.2012 00:58:19

Pavel Beran:

taky si mohl napsat jak, to miluju takovýdle debily
15.2.2015 20:55:05

Wautip:

A ono je to přitom někdy tak jednoduchý
10.7.2015 22:11:22
avatar © 2005-2024 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.