Jak udelat on-the-fly zip stream, aby fungoval s action

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
drasha
Člen | 10
+
0
-

Cau,

potreboval bych, aby mi ma aplikace na kliknuti vracela zip vybranych souboru. Doposud jsem to delal tak, ze jsem si vytvoril zip soubor a vratil ho pres FileResponse:

<?php
public function actionDownloadFiles()
{
  $zip = new \ZipArchive();
  $zip->open($zipPath, \ZipArchive::CREATE);
  $zip->addFile(...);
  $zip->close();

  this->sendResponse(new Nette\Application\Responses\FileResponse($zipPath, 'file.zip'));
?>

Problem je, ze skript ceka dokud nedobehne $zip->close a pripadne, podle nastaveni php to timeoutuje. Chtel jsem proto, aby se to balilo prubezne a vysledek se posilal na vystup, jako zde.

Narazil jsem ale na to, ze nevim, jak presvedcit Nette, aby s tim fungovalo, tj. pokud nevratim nejakou Response, tak se action nechova jak action, ale hlasi mi to page not found.

Jakekoliv napady uvitam.

Diky,
Drasha

her-ur
Člen | 20
+
0
-

Nejdriv nastav spravne hlavicky, pak posilej data na vystup a zavolej na konci akce presenteru:

<?php
$this->terminate();
?>

Takhle to pouzivam zreba pri generovani velkeho CSV souboru.

Editoval her-ur (11. 5. 2016 18:00)

Felix
Nette Core | 1247
+
+7
-

her-ur napsal(a):

Nejdriv nastav spravne hlavicky, pak posilej data na vystup a zavolej na konci akce presenteru:

<?php
$this->terminate();
?>

Takhle to pouzivam zreba pri generovani velkeho CSV souboru.

Imho lepsi nette-way je.

Udelat si FlyFileReponse, ktera implementuje IResponse, podobne jako FileResponse.

A tam si nastavis prislusne hlavicky pres response objekt. A zaroven to ukoncis.

V presenteru/controlu bude cisto a nemusis pouzivat Presenter::terminate().


Napriklad takto:

public function handleDownload() {
	$response = new FlyFileResponse($mycallback);
	$this->sendResponse($response);
}

Editoval Felix (13. 5. 2016 13:27)

drasha
Člen | 10
+
0
-

Diky obema!

Felix
Nette Core | 1247
+
+3
-

Parkrat uz jsem neco takoveho potreboval take, tak jsem to zkusil udelat do vlastniho addonu.

Mrkni na to, jestli ti to treba nepomuze.


FlyResponse & FlyFileResponse

https://github.com/…fly-response

drasha
Člen | 10
+
0
-

Diky!

drasha
Člen | 10
+
0
-

Ahoj,

znova jeste oteviram toto tema, protoze mi to stale nefunguje podle mych predstav. Nakonec jsem nevyuzil ten addon, ale muj kod je v principu to same (aspon myslim, php neni uplne ma nejsilnejsi parketa).

public function actionDownloadAllFiles()
{
    // ... Kus vynechaneho kodu

    foreach ($files as $name => $file)
    {
      if (!$file->isDir())
      {
        $filePath = $file->getRealPath();
        $zipCommand .= '"' . $filePath . '"' . " ";
      }
    }

    header('Content-Type: application/octet-stream');
    header('Content-disposition: attachment; filename="' . $downloadName . '.zip"');

    $zipCommand = "zip -r -j - ";
    $fp = popen($zipCommand, 'r');

    $buffSize = 8192;
    $buff     = '';
    while (!feof($fp))
    {
      $buff = fread($fp, $buffSize);
      echo $buff;
    }
    pclose($fp);

    $this->terminate();
}

Takto se mi soubor vytvari a stahuje uplne krasne, ale zasekne mi to php. Tj. veskere zpracovani php ceka, az se mi ten soubor dotaha. Netreba rikat, ze kdyz se takhle ma stahnout x giga, tak se to dost vlece. V podstate bych potreboval vedet, jak to udelat, aby se ten soubor stahoval nezavisle na fungovani zbytku aplikace.

Uvazoval jsem o tom, ze bych mel cgi skript, ktery by se staral jen o vytvareni a posilani tech zip souboru a nechal apache, at si to vyresi, jak to resi, kdyz stahuje velke soubory, ale vlastne nevim, jak to rozchodit v kontextu Nette. No a pak jsem si rekl, ze radsi nebudu vymyslet hovadiny a zeptam se vas, co s tim mate vetsi zkusenosti…

Felix
Nette Core | 1247
+
0
-

drasha napsal(a):

Na tohle bych nejspis pouzil nejaky non-blocking I/O nastroj. Napriklad ReactPHP.

norbe
Backer | 405
+
+2
-

@drasha Problém je pravděpodobně v tom, že máš nastartovanou sessions, stačí ji před započetím stahování zavřít…

drasha
Člen | 10
+
0
-

@norbe Mohl bys to prosim trochu rozvest? Dival jsem se na Nette\Http\Session a metody close a destroy, ale uplne mi neni jasne, jak by to mohlo pomoct, pripadne, jak to do toho kodu zaclenit.

norbe
Backer | 405
+
+1
-

Jde o to, že sessions se při nastartování zamkne a ostatní požadavky od toho samého člověka čekají, dokud nedojde k jejímu uvolnění (je to z toho důvodu, aby si ty požadavky vzájemně nepřepisovali data).

První věc co budeš mít v té metodě actionDownloadAllFiles bude:
$this->session->close();

V $this->session musíš mít Nette\Http\Session, to tam dostaneš například takto – https://doc.nette.org/…n/presenters#….

drasha
Člen | 10
+
0
-

Diky! Vypada to, ze tohle uz funguje presne podle ocekavani. Jen bych se chtel zeptat, jestli to s sebou nenese nejake problemy, krom teda rizika prepisu dat, ktere v mem pripade neni aktualni.

norbe
Backer | 405
+
0
-

Nenapadá mne žádný problém, který by to mohlo způsobit. Pokud už nepotřebuješ do sessions zapisovat, je její zavření naprosto v pořádku..

drasha
Člen | 10
+
0
-

Skvele… Jeste jednou diky.