logo
10.05.2021 18:37
1
Tak řeším problém:

1. Načte se mi stránka, která v sobě API call

2. Díky tomu API call vím, že potřebuji ze služby 3. strany stáhnout a uložit k sobě na server 10 souborů (nebo 1, popřípadě 100)

3. Nicméně ty soubory nejsou malé a když je jich hodně, tak logicky nemůžu čekat, než se vše načte a pak uživateli doručit stránku

Měl jsem nápad, že tohle už někdo řešil - https://stackoverflow.com/questions/...g-for-response.

No, bohužel ani jedna varianta nepomohla. Takže zkouším zde. Cílem je asynchroně stahovat soubory, které si pak uživatel po načtení stránky může prohlédnout (je tam na ně odkaz).

Další varianta řešení je JS, do čehož se mi moc nechce, ale pokud nic nevyjde, tak so zbejvá :D

Co se právě děje na Webtrhu?

10.05.2021 19:30
2
Osobne by som to spravil cez queue napr. RabbitMQ + Consumer čo spracuje dane requesty na stiahnutie. Ten môže bežať napr. cez Supervisor
10.05.2021 20:31
3
a tohle je veřejně dostupné nebo na přihlášení? Soubory ukládáš přímo k sobě nebo je nějak vkládáš do stránky a hned doručuješ ve výstupu (embedded base64).

V php se strašně blbě řeší asynchronní věci, ve výchozím stavu (a i většina hostingů to tak dělá) se dokonce běh php scriptu ukončuje, když klient ukončí TCP spojení. Na doinstalování čehokoliv dalšího potřebuješ na správu vlastní server, o tom nepíšeš.

V čistém php se tohle řeší vesměs tak, že ve smyčce provoláváš nějakou url a ta zajišťuje stahování. Buď vrátíš html stránku a z ní budeš javascriptem volat svůj server a ten bude postupně obsah stahovat, až se stáhne všechen, můžeš ho zobrazit a skončit provolávání. Nebo si uděláš společnou frontu pro všechny (může být klidně v mysql), uděláš si php script, který tuhle frontu bude po X položkách odbavovat (buď lze nastavit přímo na hostingách něco jako php cron nebo si to budeš něčím provolávat pravidelně sám). Poté u uživatele stejně, pokud není ještě vše stáhnuté, vrátíš html stránku, kde budeš uživatele informovat a sama si bude ověřovat přes ajax, jestli už není vše.

JS se můžeš vyhnout, při zobrazování stránky ověříš, jestli jsou všechny soubory stažené a buď vrátíš stránku s jejich úplným seznamem nebo jinou stránku s nějakou zprávu (a klidně částečným seznamem) a do hlavičky dáš meta tag refresh 30s a ona se ti sama po půl minutě obnoví, pokud již budou všechny staženy, vrátíš finální stránku, pokud ne, znovu pošlešl dočasnou s meta refresh. Tady ale musíš mít nějaký php cron, který to bude na pozadí stahovat.

Tohle by není ani složité napsat v php a mysql, je to poměrně základní algoritmus a nemělo by to být drahé. Jestli na to je třeba do WP či jinám hotový plugin, těžko říct. Výhoda řešení přes databázi je možnost mít zobrazené počet stažených souborů, počet souborů ve frontě a mít nad tím lepší přehled. Není potřeba instalovat a používat něco jako RabbitMQ a další specializované nástroje, ty mají trochu jiný záměr a jsou to další technologie, které zvyšují složitost systému.
10.05.2021 20:41
4
No, jsou to vygenerované PDF uložené na mém serveru ve složce. Bohužel využít databázi aktuálně nedává smysl a jelikož může mít user 20 PDF (odzkoušeno) kde se kadé PDF stahuje 5s ... tak opravdu 1.5min čekání na doběhnutí scriptu je nesmysl.

Proto sem to chtěl dělat takhle, nicméně tedy asi nyní bude stačit tu frontu řešit přes soubor místo SQL - je to proof of concept. Ale doufal jsem, že by třeba šlo jít cestou, že pingnu PHP URL a server si to zařadí do fronty mimo a můj nechá bejt - oba scripty jsou ve stejné složce.
10.05.2021 21:55
5
ano, můžeš to řešit přes soubor, pamatovat si v něm seznam pdf ke stažení, při zavolání skriptu jich pár stáhnout a tenhle script volat z webu (či někde centralně).

Pokud máš kontrolu nad serverem, lze to tak nějak udělat. Mrkni na php funkci ignore_user_abort(), ta zajistí, že script poběží i po ukončení spojení od klienta. Spojení s klientem ukončím buď v fpm přes funkci fastcgi_end_request() nebo ruční vrácení header('Connection: close'); v kombinaci s ob_start abys mohl řádně poslat content-length (kód kdyžtak napíšu, kdybys chtěl, snad to tak funguje i v moderních prohlížečích). Po odpojení klienta (ten už nečeká na načtení stránky) si můžeš soubory stahovat. Má to ale jednu nevýhodu, občas může stahování selhat a nedozvíš se to, v php-fpm můžeš nastavit retry, ale to opět nemusí být plně spolehlivé.

Pak by mělo stačit jen rychle provolat řadu url na stažení, php si to dá do fronty (k tomu je potřeba php-fpm) a postupně provolá všechny. Blbé je, že ti ta fronta bude blokovat i ostatní stránky, takže je lepší udělat extra fpm instanci. No a celé to je ve výsledku tak složité, že už je jednodušší to udělat standardněji.

To chceš dělat sám? Jak moc dobře umíš php? Nápadů mám spousty, ale bojím se, že vlastně potřebuješ něco co budeš schopný udělat sám a proto se ti to tak komplikuje.
10.05.2021 22:05
6
Původně odeslal TomášX
ano, můžeš to řešit přes soubor, pamatovat si v něm seznam pdf ke stažení, při zavolání skriptu jich pár stáhnout a tenhle script volat z webu (či někde centralně).

Pokud máš kontrolu nad serverem, lze to tak nějak udělat. Mrkni na php funkci ignore_user_abort(), ta zajistí, že script poběží i po ukončení spojení od klienta. Spojení s klientem ukončím buď v fpm přes funkci fastcgi_end_request() nebo ruční vrácení header('Connection: close'); v kombinaci s ob_start abys mohl řádně poslat content-length (kód kdyžtak napíšu, kdybys chtěl, snad to tak funguje i v moderních prohlížečích). Po odpojení klienta (ten už nečeká na načtení stránky) si můžeš soubory stahovat. Má to ale jednu nevýhodu, občas může stahování selhat a nedozvíš se to, v php-fpm můžeš nastavit retry, ale to opět nemusí být plně spolehlivé.

Pak by mělo stačit jen rychle provolat řadu url na stažení, php si to dá do fronty (k tomu je potřeba php-fpm) a postupně provolá všechny. Blbé je, že ti ta fronta bude blokovat i ostatní stránky, takže je lepší udělat extra fpm instanci. No a celé to je ve výsledku tak složité, že už je jednodušší to udělat standardněji.

To chceš dělat sám? Jak moc dobře umíš php? Nápadů mám spousty, ale bojím se, že vlastně potřebuješ něco co budeš schopný udělat sám a proto se ti to tak komplikuje.
Tak něco v tom umím, ale plně v tom nedělám. Jinak sem právě třeba zkoušel "ignore_user_abort" ... ale nějak to nefachá (mám nasazeno). Je to prototyp aplikace, když se potvrdí, že koncept funguje, tak se bude řešit plné věcí pořádně / jinak.

Ale pro teď sem jen chtěl zkusit něco easy na vložení do kodu, protože to není zas až tak důležitý a je to něco navíc.
10.05.2021 22:19
7
a jak jsi to zkoušel? Ideálně to zavolej na začátku skriptu, nech něco stahovat co trvá alespoň několik desítek vteřin, v prohlížeči otevři tu url a chvilku počkej a zavři tab, skript by měl dál běžet a soubory by se měly dostáhnout
10.05.2021 23:28
8
Doctore97, pro to paralelní zpracování (stahování) asi pomůže PHP kód níže.
Bude to ale závislé na nastavení PHP-FPM, co píše TomášX (počet povolených procesů).

Tohle zavolá víc url paralelně, ty stáhnou podle předaných POST dat PDF soubory a dají vědět zpět.

PHP kód:
function openNewSocket($host null$port 443$block false) {
    
$sock false;
    if (!empty(
$host) && !empty($port)) {
        
//open new sock
        
$sock fsockopen($host$port);
        
//set non-blocking socket
        
if ($sockstream_set_blocking($sock$block);
    }
    return(
$sock);
}

function 
sendPostData($host null$port 443$path null$data null) {
    
$sock openNewSocket($host$portfalse);
    if (
$sock) {
        
$content null;
        
//create url encoded string
        
if (is_array($data) && !empty($data)) {
            
$content http_build_query($data);
        }
        
//send header
        
fwrite($sock"POST /".$path." HTTP/1.1\r\n");
        
fwrite($sock"Host: ".$host."\r\n");
        
fwrite($sock"Content-Type: application/x-www-form-urlencoded\r\n");
        
fwrite($sock"Content-Length: ".strlen($content)."\r\n");
        
fwrite($sock"Connection: close\r\n");
        
fwrite($sock"\r\n");
        
//append content if exists
        
if (isset($content)) {
            
fwrite($sock$content);
        }
        unset(
$content);
    }
    return(
$sock);
}

$worker_host 'ssl://www.domena.xy/';        //worker domain
$worker_script 'download.php';            //worker script

//files for download
$download_files = array(
    
"https://www.example.com/codument1.pdf";
    , 
"https://www.example.com/codument2.pdf";
    , 
"https://www.example.com/codument3.pdf";
);

//open parallel sockets
$sockets = array();
foreach (
$download_files as $file) {
    
$sockets sendPostData$worker_host443$worker_script, array("uri" => $file) ); 
}

//list of sockets
var_dump($sockets);

//...wait for close all sockets... 
download.php je worker script, který jen stáhne třeba pomocí CURL soubor (uri dostane POSTem) a vrátí volajícímu scriptu URI staženého souboru na serveru.

Dal jsem to dokupy jen narychlo, tak tam možná bude chybět nějaká čárka, tečka, ale princip snad bude pochopitelný.
11.05.2021 11:57
9
Co tak udělat to následovně:

1. Požádat server o data requestem ze stránky
2. Zobrazit uživateli stránku s informací, že se to načte
3. Poslat obsah (respektive asi linky na obsah) přes https://pusher.com/
15.05.2021 19:16
10
Mě napadá řešení přenést bod 2. z tvého původního zadání na stránku klienta, přes JavaScript/AJAX:

1. Načteš stránku, ve které se přes výsledek API dozvíš, které soubory máš vůbec připravit.
2. Zobrazíš seznam těchto souborů klientovi (jméno, meta informace, animované kolečko, … – stylem, který chceš/potřebuješ) a u každého (od klienta, AJAXem) zavoláš požadavek na jeho stažení.
3. Tak, jak ti budou přicházet odpovědi, že ten který soubor je připraven, u klienta zobrazíš link na něj.