Zipování souborů a jejich následné stažení
- Webster.K
- Člen | 212
Zdravím všechny, dávám dohromady script, který by měl podle parametru stáhnout určitá data z databáze (objednávky). mám tento kód:
private function zipArchiv() {
//načtení hodnot z DB
$objednavky = $this->database->table('objednavky')->where('docasna', 0);
//vytvoření PDFek a jejich uložení na server přes knihovnu mpdf
foreach ($objednavky as $objednavka) {
$this->exportPDF($objednavka->id_objednavky);
}
//načtení obsahu adresáře
$dir = './files/pdf/';
foreach (Finder::findFiles('*.pdf')->in($dir) as $key => $file) {
//echo $key; // $key je řetězec s názvem souboru včetně cesty
//echo $file; // $file je objektem SplFileInfo
$files[] = $file;
}
//komprese
$zipname = 'archiv_s_objednavkama.zip';
$zip = new \ZipArchive;
$zip->open($zipname, \ZipArchive::CREATE);
foreach ($files as $soubor) {
$zip->addFile($soubor);
}
$zip->close();
///uložení zazipovaného souboru
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename=' . $zipname);
header('Content-Length: ' . filesize($zipname));
readfile($zipname);
}
Vše se zdá být v pořádku. Objednávky se z DB načtou, přes exportPDF (mpdf knihovna) to do složky na serveru uloží soubory které budou potřeba pro archivování, pak se otevře adresář, do kterého se vše přidá, soubor se vytvoří a uloží. Vše na první pohled vypadá, že je ok. Problém je, že ten vytvořený soubor obsahuje nejdříve adresář „.“, potom adresář files a potom adresář pdf a v něm ty samotné soubory. To by samo o sobě nebyl problém. Problém nastane, když chci jednotlivé soubory otevřít, v tu chvíli to hodí chybu archívu. Soubor má nejspíše správnou velikost, nicméně stažení souboru z archívu se už nepovede, někdo nějaký nápad?
- Webster.K
- Člen | 212
Tak už jsem na to přišel a funguje to v pohodě :)
private function zipArchiv() {
\Nette\Utils\FileSystem::delete('./files/pdf/');
\Nette\Utils\FileSystem::createDir('./files/pdf/');
$objednavky = $this->database->table('objednavky')->where('docasna', 1);
foreach ($objednavky as $objednavka) {
$this->exportPDF($objednavka->id_objednavky);
}
$dir = './files/pdf/';
foreach (Finder::findFiles('*.pdf')->in($dir) as $key => $file) {
//echo $key; // $key je řetězec s názvem souboru včetně cesty
//echo $file; // $file je objektem SplFileInfo
$files[] = ['path'=>$key,'file'=>\Nette\Utils\FileSystem::read($file),'name'=>basename($key)];
}
$zipname = 'archiv_s_objednavkama.zip';
$zip = new \ZipArchive;
$zip->open($dir . $zipname, \ZipArchive::CREATE);
foreach ($files as $soubor) {
$zip->addFile($soubor['path'],$soubor['name']);
//$zip->addFromString($soubor['path'],$soubor['file']);
}
$zip->close();
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename=' . $zipname);
header('Content-Length: ' . filesize($dir . $zipname));
readfile($dir . $zipname);
}
- Webster.K
- Člen | 212
Tak jsem to trochu testoval a při velkém množství souboru je to poměrně hodně pomalé :/ dlouho se čeká, než se soubor vytvoří. Jde nějakým způsobem udělat, jako je to třeba na google drive, když se připravuje velký soubor, tak něco podobného? Zjistil jsem, že při extrémních souborech to hodí 504 Bad Gateway – timeout
- dms
- Člen | 94
Ahoj, na jednom projektu pouzivame maennchen/zipstream-php Funguje to tak ze se zip generuje a zaroven streamuje, tzn zacne se hned stahovat a uzivatel neceka nez probehne nejaka dlouha akce, ale vidi ze se mu soubor stahuje hned. Generujeme to pro galerie, kde je stovky fotek a x giga objemy dat takze generovani zipu trvalo dlouho, ale tohle to vyresilo. Pouziti knihovny je stejne jako u ziparchivu, ve finale jeste jednodussi. Jediny problem je, ze se dopredu nevi celkova velikost souboru, takze uzivatel stahuje soubor, ale nema prehled o tom kdy to dobehne.
- Webster.K
- Člen | 212
To že by uživatel nevěděl, kdy to doběhne mi nevadí, potřebuji hlavně, aby to vyvolalo akci na uložení souboru, to že to bude pak něco dělat je mi už jedno. Jsem to zkusil použít ale bez úspěchu :/ chová se mi to úplně stejně jako to řešení, co jsem měl před tím. Mám tenhle kód:
public function renderZip() {
$this->zipArchiv();
}
private function zipArchiv() {
\Nette\Utils\FileSystem::delete('./files/pdf/');
\Nette\Utils\FileSystem::createDir('./files/pdf/');
$objednavky = $this->database->table('objednavky')->where('docasna', 1);
foreach ($objednavky as $objednavka) {
$this->exportPDF($objednavka->id_objednavky);
}
$dir = './files/pdf/';
foreach (Finder::findFiles('*.pdf')->in($dir) as $key => $file) {
$files[] = ['path' => $key, 'file' => \Nette\Utils\FileSystem::read($file), 'name' => basename($key)];
}
$zipname = 'archiv_s_objednavkama.zip';
$zip = new \ZipStream\ZipStream($zipname);
foreach ($files as $soubor) {
$zip->addFileFromPath($soubor['name'],$soubor['path']);
}
$zip->finish();
}
- Webster.K
- Člen | 212
tak jsem to opravil na toto
private function zipArchiv() {
\Nette\Utils\FileSystem::delete('./files/pdf/');
\Nette\Utils\FileSystem::createDir('./files/pdf/');
$objednavky = $this->database->table('objednavky')->where('docasna', 1);
$zipname = 'archiv_s_objednavkama.zip';
$zip = new \ZipStream\ZipStream($zipname);
$dir = './files/pdf/';
foreach ($objednavky as $objednavka) {
$this->exportPDF($objednavka->id_objednavky);
foreach (Finder::findFiles($objednavka->cislo_zakazky . '.pdf')->in($dir) as $key) {
$file = ['path' => $key, 'name' => basename($key)];
break;
}
$zip->addFileFromPath($file['name'], $file['path']);
}
$zip->finish();
}
a funguje to v pořádku :) děkuji všem za rady. Spolu s tím jsem ale přišel ještě na jednu nepříjemnou věc a to je, že po dobu stahování ten konkrétní uživatel, který si soubor stahuje nemůže zobrazit nic jiného, pokračování práce na webu tedy může pokračovat až ve chvíli, kdy se soubor dostáhne
Editoval Webster.K (6. 11. 2018 7:35)
- MajklNajt
- Člen | 502
Webster.K napsal(a):
a funguje to v pořádku :) děkuji všem za rady. Spolu s tím jsem ale přišel ještě na jednu nepříjemnou věc a to je, že po dobu stahování ten konkrétní uživatel, který si soubor stahuje nemůže zobrazit nic jiného, pokračování práce na webu tedy může pokračovat až ve chvíli, kdy se soubor dostáhne
to bude kvôli zámku sessions, skús pred tým odosielaním použiť session_write_close() resp. čistejšie Nette\Http\Session::close()
- MajklNajt
- Člen | 502
funkcia session_write_close() ti zatvorí session pre zápis, čiže už nebudeš môcť robiť zmenu dát v session (čo v momente posielania zipu už asi nepotrebuješ, prihláseného užívateľa testuješ na začiatku životného cyklu presenteru príp. v modeli)
- Webster.K
- Člen | 212
Aha, vlastně jo no :D takže tam to už nevadí. Ok, to jsem provedl, hodil
jsem tam po začátku vytvoření zipu tedy \Nette\http\Session::close(); a
hodilo to chybu:
Deprecated
Non-static method Nette\Http\Session::close() should not be called statically,
assuming $this from incompatible context
a
$sess = new \Nette\Http\Session;
$sess->close();
hodí: Argument 1 passed to Nette\Http\Session::__construct() must implement interface Nette\Http\IRequest, none given, called in /var/www/html/pavel_web/app/presenters/ObjednavkyPresenter.php on line 239 and defined
Editoval Webster.K (6. 11. 2018 9:19)
- MajklNajt
- Člen | 502
Webster.K napsal(a):
Aha, vlastně jo no :D takže tam to už nevadí. Ok, to jsem provedl, hodil jsem tam po začátku vytvoření zipu tedy \Nette\http\Session::close(); a hodilo to chybu:
Deprecated
Non-static method Nette\Http\Session::close() should not be called statically, assuming $this from incompatible context
Nette\http\Session
si musíš vyžiadať z DI (cez constructor
alebo @inject
) potom
volať $this->session->close()
Editoval MajklNajt (6. 11. 2018 9:19)
- japlavaren
- Člen | 404
Webster.K napsal(a):
tak jsem to opravil na toto
private function zipArchiv() { \Nette\Utils\FileSystem::delete('./files/pdf/'); \Nette\Utils\FileSystem::createDir('./files/pdf/'); $objednavky = $this->database->table('objednavky')->where('docasna', 1); $zipname = 'archiv_s_objednavkama.zip'; $zip = new \ZipStream\ZipStream($zipname); $dir = './files/pdf/'; foreach ($objednavky as $objednavka) { $this->exportPDF($objednavka->id_objednavky); foreach (Finder::findFiles($objednavka->cislo_zakazky . '.pdf')->in($dir) as $key) { $file = ['path' => $key, 'name' => basename($key)]; break; } $zip->addFileFromPath($file['name'], $file['path']); } $zip->finish(); }
a funguje to v pořádku :) děkuji všem za rady. Spolu s tím jsem ale přišel ještě na jednu nepříjemnou věc a to je, že po dobu stahování ten konkrétní uživatel, který si soubor stahuje nemůže zobrazit nic jiného, pokračování práce na webu tedy může pokračovat až ve chvíli, kdy se soubor dostáhne
tento riadok mi prijde zbytocny – naco to prehladavas ked vies ze to vrati iba jeden subor?
<?php
foreach (Finder::findFiles($objednavka->cislo_zakazky . '.pdf')->in($dir) as $key) {
$file = ['path' => $key, 'name' => basename($key)];
break;
}
?>