Zobrazení průběhu uploadu prakticky

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

Možnosti pro zobrazení indikátoru, který by uživateli zobrazoval průběh odesílání formulářových dat (a především souborů v nich), jsem již rozebíral, nyní bych se rád zaměřil na praktickou ukázku v PHP 5.2.0 a novějším spolu s rozšířením APC. Ještě než se podíváme na ukázku kódu, musím si postesknout nad tím, že informaci o průběhu odesílání dat nezobrazuje přímo prohlížeč. Na rozdíl od situace při stahování dat má přesnou informaci o tom, kolik dat celkem je a jak je s jejich posíláním daleko.

Pokud je zapnuta konfigurační direktiva apc.rfc1867 a formulář obsahuje pole APC_UPLOAD_PROGRESS, tak bude APC při přenášení všech následujících souborů ve formuláři aktualizovat sdílenou proměnnou upload_key, kde key je hodnota zmíněného formulářového pole. K této proměnné můžeme následně přistoupit ze všech skriptů funkcí apc_fetch.

Pokud chceme průběh nahrávání souboru zobrazovat přímo ve stránce s formulářem, dá se to nejpohodlněji zařídit pomocí technologie AJAX. V pravidelných intervalech se budeme serveru dotazovat na stav přenosu a tuto informaci budeme zobrazovat na stránce.

<?php
$id = uniqid();
?>

<script type="text/javascript">
function progress_update(xmlhttp) {
	if (xmlhttp.readyState == 4) {
		document.getElementById('progress').innerHTML = xmlhttp.responseText;
		window.setTimeout(progress_send, 1000);
	}
}
function progress_send() {
	send_xmlhttprequest(progress_update, 'GET', 'progress.php?id=<?php echo $id; ?>');
}
</script>

<form action="" method="post" enctype="multipart/form-data">
<input type="hidden" name="APC_UPLOAD_PROGRESS" value="<?php echo $id; ?>">
<input type="file" name="file">
<input type="submit" onclick="window.setTimeout(progress_send, 1000);">
<span id="progress"></span>
</form>

Viz send_xmlhttprequest.

Pro identifikátor přenosu by se dalo použít např. session_id, nicméně pro případ, že by jeden uživatel odesílal více formulářů, je lepší použít nějaký zcela jednoznačný identifikátor.

Pravidelná aktualizace stavu přenosu svádí k použití funkce setInterval, u té bychom ale museli řešit případ, kdy se vyřízení dřívějšího požadavku chvíli pozdrží, takže jeho odpověď potom přepíše aktuálnější hodnotu. Proto budeme znovu čekat až poté, co se zobrazí předchozí stav.

Skript pro vracení dat už bude poměrně jednoduchý – z informací v poli upload_$_GET[id] vypočteme procento přenesených dat a vypíšeme ho.

<?php
$progress = apc_fetch("upload_$_GET[id]");
echo ($progress ? number_format(100 * $progress["current"] / $progress["total"], 2) . "%" : "");
?>

Pokud bychom chtěli zobrazovat i další údaje, vrátili bychom XML soubor nebo JSON data se všemi informacemi – pole obsahuje kromě celkového objemu dat a už přenesené velikosti také položky pro přenosovou rychlost (rate), název formulářového pole (name), dočasnou cestu (temp_filename) a původní jméno právě přenášeného souboru (filename) a také informace o zrušeném přenosu (cancel_upload) a dokončení (done).

Při pokusech s odesíláním velkých souborů nezapomeňte správně nastavit maximální velikost nahrávaných souborů.

Jakub Vrána, Řešení problému, 23.4.2007, diskuse: 20 (nové: 0)

Diskuse

ikona LLook:

> musím si postesknout nad tím, že informaci o průběhu odesílání dat nezobrazuje přímo prohlížeč

A já se musím pochlubit, že můj prohlížeč to zobrazuje. Opera, když si nastavím Zobrazit -> Lišty -> Přizpůsobit -> Průběh načítání: Pod načítanou stránkou.

Matěj:

Dobrý den, když si stáhnu soubory APC-3.1.4 jak to mam nainstalovat, nebo prostě rozchodit? Není k tomu někde nějakej návod jak to apc nainstalovat a používat?

Děkuji

ikona Jakub Vrána OpenID:

Záleží na tom, na jakém operačním systému. Pro Windows jsou k dispozici balíky na http://downloads.php.net/pierre/

ikona Michal Kniha:

Firefox 3.0 by to již měl umět také.

Tomáš Müller:

Neni v tom druhem snippetu chybicka?

$progress = apc_fetch("upload_$_GET[id]");
->
$progress = apc_fetch("upload_{$_GET[id]}");
?

Tomáš Müller:

To jsem se asi trochu unahlil, PHP to zkousne, ale ja bych to preci jen napsal takhle:
$progress = apc_fetch("upload_{$_GET['id']}");

ikona Jakub Vrána OpenID:

Chybný by byl naopak způsob "upload_{$_GET[id]}", protože by se v něm id bralo jako konstanta. Když už, tak "upload_{$_GET["id"]}" nebo spíš rovnou "upload_" . $_GET["id"], ale je to samoúčelné, "upload_$_GET[id]" je zcela v pořádku.

Tomáš Müller:

Ano, PHP by vyhodilo notice, mel jsem za to, ze ho hodi i pri tom vasem zpusobu zapisu, ale neni tomu tak.

Kazdopadne diky za zajimavy clanek, rozhodne si to vyzkousim.

Tomáš Müller:

Podelim se o prakticke zkusenosti, podpora apc.rfc1867 zrejme existuje az od apc-3.0.13 ( i kdyz v changelogu o tom neni zminka, viz komentar zde: http://pecl.php.net/bugs/bug.php?id=9312&edit=1 ) a samotna APC se mi nejevi jako prilis stabilni. http://pecl.php.net/bugs/bug.php?id=10715 :)

ikona Jakub Vrána OpenID:

Minimální verze je uvedena v dokumentaci, bohužel se to ještě nedostalo na web: http://cvs.php.net/phpdoc/en/reference/apc/ini.xml?view=markup.  S PHP 5.2.1 a APC 3.0.13 se mi bug pod Windows nepodařilo zreprodukovat.

Tomáš Müller:

Takze jsem zpet, nyni ale doufam, ze muj komentar bude skutecne k veci a prinosny :)
Pokud instalujete APC jenom pro tuhle featuru, doporucuji upravit konfiguraci. Zejmena apc.shm_size - ta urcuje, jakou velikost ma jeden segment shared memory pouzivane pro cache. Defaultni hodnota je 30 a je to v megabajtech :) apc.shm_segments je pocet alokovanych segmentu, default je jeden.

tygr:

Muzete mi prosim poradit, kde najdu konfiguracni direktivu apc.rfc1867, kde ma byt umisten skript pro vraceni dat a zda na zprovozneni podpory APC v PHP staci v php.ini pridat extension=php_apc.dll?

Filip Krejčí:

Problem je ale v tom ze apc.rfc1867 neni thread safe, a posledni upload rusi stavy vsech predchozich. Tudiz je to cele o nicem.

Vlastovka:

Dobrý den,
Rád bych věděl, zda se nějak může uploadovat více souborů v 1 formuláři (resp. jak predat id vice souboru v 1 formulari - zde je jen 1 pole s nazvem APC_UPLOAD_PROGRESS)

díky

ikona Jakub Vrána OpenID:

Pokud vím, tak ne.

Andy:

Ovsem ze jde, staci formular upravit napriklad takto <input type="hidden" name="APC_UPLOAD_PROGRESS" value="nejaky id" />
<input type="file" name="file[]" />
<input type="file" name="file[]" />

apc_fetch() pak bude zobrazovat celkovy progress upload a prave uploadovany soubor napriklad

array(6) {
  ["total"]=>
  int(627270)
  ["current"]=>
  int(41326)
  ["filename"]=>
  string(12) "PUTTYGEN.EXE"
  ["name"]=>
  string(6) "file[]"
  ["done"]=>
  int(0)
  ["start_time"]=>
  float(1240498405.4505)
}

jak zjistit prubeh uploadu aktualniho souboru sem zatim nezjistoval, pripadne pro upload vice souboru lze udelat vice formularu a pomoci javascriptu je odesilat postupne :-)

tomasr:

Je nejake omezeni pro to APC na strane serveru? Kdyz sem to zkousel, tak behem uploadu se nedelo nic, az nakonci skocilo 100%, zadnej progress neni videt:(.

Andy:

Mám stejný problém, funkce apc_fetch() po celou dobu uploadu nic nevrací až po dokončení uploadu vrátí výsledek.

Andy:

Uz vim cim to je, je treba uvest
<input type="hidden" name="APC_UPLOAD_PROGRESS" value="id" />
PRED POLEM
<input type="file" name="file" />
protoze prohlizec posila jednotliva pole za sebou stejne jak jsou uvedena na strance, to znamena, pokud uvedete APC_UPLOAD_PROGRESS az za file, prohlizec posle id az po uploadu souboru

SwimX:

Zapl jsem direktivu apc.rfc1867 a formulář obsahuje pole APC_UPLOAD_PROGRESS přesto mi apc_fetch nic nevrátí. Může někdo poradit? (phpinfo APC module = http://www.srdcari.cz/info.php#module_apc )

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, 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:

avatar © 2005-2018 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.