Progress bar / text info během scriptu v Nette
- MW
- Člen | 626
Zdravím a prosím o pomoc..
sice jsem nějaká nakousnutá témata nalezl, ale řešení bohužel ne.
Jednoduše potřebuji nějaký progres či informaci zobrazovat během cyklu ve
scriptu.
naivně jsem zkusil
for ($i=1; $i<10; $i++) {
$this->flashMessage('bla'.$i);
$this->invalidateControl('flash');
sleep(1);
}
Což ve výsledku vyplivne až na konci.. logicky…
Při pokusech s ob_flush a flus úspěšný nejsem..
Poradil by někdo, jak to udělat, prosím?
Editoval MW (17. 6. 2015 13:25)
- iguana007
- Člen | 970
Tímto způsobem, jak si již napsal, to „logicky“ fungovat nemůže, protože z pohledu životního cyklu aplikace se jedna o jeden request/životní cyklus, kde je pouze jeden request a jedna response.
Aby si nějakou takovou informaci mohl v prohlížeči vykreslovat, tak bys
ten složitý/dlouho běžící loop musel spustit někde na pozadí (třeba
CRONem) a někam si ukládat mezistavy v jednotlivých cyklech.
V prohlížeči si pak volal ajaxem nějaký handle, který bude vracet
číselný status dané operace a ty jej jen vykreslíš.
Složitější varianta by byla s využitím nějakého AJAX push engine, který by ti ten progres v browseru updatoval v browseru sám (odpadlo by ti to AJAX volání handle v intervalech – funguje to obdobně, jako push notifikace na iOS a Androidu) – jeden z nejznámějších je třeba: http://ape-project.org/ – ale jsou jich mraky jiných a je na tobě, který by sis vybral.
- David Matějka
- Moderator | 6445
Behem jednoho http pozadavku to bude sranda :)
slo by vyuzit ProgressEvent, ukazka pouziti: http://www.dave-bond.com/…gress-HMTL5/ Z php scriptu pak budes muset posilat treba po jednom znaku, coz bude signalizovat ten progres. Je tam ale nekolik problemu – musis zajistit, aby php ihned odesilalo na vystup, takze vypnout ruzny output_buffering, gzip a nejen pro php, ale i pro webserver. A pak bude problem, pokud budes chtit posilat dalsi response, jelikoz uz pred tim budes mit bordel atd, atd.
Ale zkusil jsem to a funguje to! :)
Radeji najdi nejaky jiny reseni, treba ze ten ukol predas ke zpracovani nejakemu rabbitmq a budes se periodicky dotazovat na stav.
Mala ukazka:
public function handleProgress()
{
for ($i = 0; $i < 100; $i++) {
echo '.';
ob_flush();
flush();
usleep(20000);
}
exit;
}
<div id="progress"></div>
<script>
$(function () {
$.ajax({
type: 'GET',
url: "/?do=progress",
data: { },
xhr: function () {
var xhr = $.ajaxSettings.xhr();
xhr.addEventListener("progress", function (evt) {
/* nepouzivam lengthComputable, jelikoz nepouzivam Content-length hlavicku */
$("#progress").html(Math.min(100, evt.loaded));
}, false);
return xhr;
},
success: function (data) {
$("#progress").html("hotovo");
}
});
});
</script>
a v nginxu jsem musel nastavit:
gzip off;
fastcgi_keep_conn on;
:P
- Freema
- Člen | 18
Já to dělám tak že v requestu ve kterým mi běží nějaký dlouhý cyklus nechávám cachovat soubor nebo do redisu jeho progress. Druhy ajax request který běží souběžně s tím prvním mi jen načítá ty data z cache v nějakým intervalu a tím pak vypočítavá délku progress barů. Je to trochu hodně psaního, ale docela to funguje lepší než ten flush() který mi přijde že není moc spolehlivý.
- MW
- Člen | 626
Dekuji !
No není to tak trivi, jak jsem si myslel…
Ale v zasade ukladani v cyklu nekam a dotazovat se JS by asi mohla byt
ta cesta.
třeba do souboru? Myslite, ze by to slo?
To u ceho to potrebuju je proste import asi mil. vet do db a tak ze bych to ukladal nekam každý Xty cyklus do db nebo souboru a JS ho v cyklech cetl?
Existuje na tohle vůbec nejaky „cisty“ reseni? :)
Diky!
- Michal Vyšinský
- Člen | 608
Použil bych sockety a z PHP procesu pushoval do klienta informace o průběhu.
- David Matějka
- Moderator | 6445
@MW jak pisu, je potreba nastavit X veci, aby nedochazelo k zadnymu bufferovani ani komprimovani. nevim, co vse bude potreba u apache, hledej.. Ale tohle jsem psal spis jako zajimavost, lepsi bude pouzit nekterou z jinych uvedenych moznosti
- MW
- Člen | 626
Trochu si s tim hraju, tak jsem si řekl, ze udelam něco trivi přes session.. zda se, ze me to funguje jen problém je v tom, ze me k tomu cislu, které vraci handleProgress() vyhodi nejen cislo, ale i celou stranku…vč menu, a dalších věci.. proste celym HTML obsahem. Asi to blbe ctu tim JS…
Poradi někdo prosim? Diky a tady je kod:
šablona:
<script type="text/javascript">
//Script
$.ajax({
url: {link longScript!},
success: function(data) {
}
});
//Progress
function getProgress(){
$.ajax({
url: {link progress!},
success: function(data) {
$("#progress").html(data);
if(data<10){
getProgress();
}
}
});
}
getProgress();
</script>
<div id="progress"></div>
presenter:
public function handleLongScript() {
$session = $this->getSession();
for($i=1;$i<=10;$i++){
$progress = $session->getSection('progress');
$progress->val = $i;
$session->close();
sleep(1);
}
}
public function handleProgress() {
echo $this->getSession('progress')->val;
}
Editoval MW (19. 6. 2015 12:05)
- Lukeluha
- Člen | 130
public function handleProgress()
{
$this->payload->data = $this->getSession('progress')->val;
$this->sendPayload(); // toto ti odešle json response
}
v JS potom
success: function(data) {
$("#progress").html(data.data);
if(data.data<10){
getProgress();
}
}
Editoval Lukeluha (19. 6. 2015 21:01)
- Myiyk
- Člen | 321
Šlo by to vyřešit bez instalací dalších věcí na server. Musíš nastavit apache, aby zpracovávalo více požadavků současně, min. 2
Přes AJAX budeš volat požadavek na tu akci, která se dlouho
zpracovává.
V pravidelných intervalech budeš volat další AJAX na dotaz, v kolika jsi
procentech.
Když první volání vrátí odpověď, ukončíš aktualizaci procent.
Funkce zpracování dat musí někde (DB, soubor, atd) zapsat informace
o procentech.
A druhá funkce která to má zjistit, se k tomu nějak musí dostat.
Nevýhodou je, že tvůj požadavek nemůžu být příliš složitý, aby se stihl provést.
Editoval Myiyk (20. 6. 2015 0:24)
- MW
- Člen | 626
Tak kdyby někdo mel zajem, toto je funkcni verze. Budu rad za jakekoliv
návrhy na zlepseni :)
Diky
latte:
<script type='text/javascript'>
//Start the long running process
function runScript(){
document.getElementById('progress').style.visibility = 'visible';
document.getElementById('actionbutton').innerHTML = 'Pracuji...';
document.getElementById('actionbutton').setAttribute('disabled', 'disabled');
$.ajax({
url: {link longScript!},
success: function(data) {
}
});
getProgress();
}
function clearSession(){
$.ajax({
url: {link clearSession!}
});
}
//Start receiving progress
function getProgress(){
$.ajax({
url: {link progress!},
success: function(data) {
$('#progressinfo').html('Začínám...');
if(data.data > 0) {
$('#progressinfo').html(data.data+ '%');
}
if(data.data < 100){
$('.progress-bar').css('width', data.data+'%').attr('aria-valuenow', data.data);
getProgress();
}
if(data.data === 100){
$('.progress-bar').css('width', '100%').attr('aria-valuenow', 100);
$('#progressinfo').html('Hotovo');
clearSession();
document.getElementById('actionbutton').innerHTML = 'Spustit znovu';
document.getElementById('actionbutton').removeAttribute('disabled');
}
}
});
}
</script>
<div id=progress class='progress progress-striped' style='width: 400px; visibility: hidden;'>
<div class='progress-bar' role='progressbar' aria-valuenow='0' aria-valuemin='0' aria-valuemax='100'>
<div id='progressinfo'></div>
</div>
</div><br />
<button id="actionbutton" type="submit" class="btn btn-warning btn-small" onclick="runScript()">Spustit akci</button><br /><br />
a presenter:
public function handleLongScript() {
sleep(1);
$session = $this->getSession();
$progress = $session->getSection('progress');
$progress->val = 0;
$session->close();
for($i=0;$i<=100;$i++){
$session = $this->getSession();
$progress = $session->getSection('progress');
$progress->val = $i;
$session->close();
usleep(1000);
}
$i = 0;
}
public function handleProgress() {
$this->payload->data = $this->getSession('progress')->val;
$this->sendPayload();
}
public function handleClearSession() {
$session = $this->getSession();
$progress = $session->getSection('progress');
$this->payload->data = NULL;
$progress->remove();
$session->close();
}
- akadlec
- Člen | 1326
Možná si tam měl nějaký překlep, toto:
document.getElementById('progress').style.visibility = 'visible';
document.getElementById('actionbutton').innerHTML = 'Pracuji...';
document.getElementById('actionbutton').setAttribute('disabled', 'disabled');
pokud to chceš v jQuery musí jít zapsat takto:
$('#progress').css({'visibility': 'visible'});
$('#actionbutton').html('Pracuji...').prop('disabled', 'disabled');