[addon multiplefileupload] MultipleFileUpload – form control
- Honza Kuchař
- Člen | 1662
Diskuse ke stránce MultipleFileUpload – form control
Ahoj, už mám skorou hotový tento MultipleFileUpload
s
jQuery
. Ale řeším takový menší problémek:
Nahraji soubory na server pomocí uploadify. Uložím je do tempů. Uživatel stránku opustí. → soubory zůstanou v tempech dokud je něco ručně neodstraní. Napadá Vás nějaké elegantní řešení jak tohle vyřešit? Napadlo mě to řešit tak, že si vytvořím v cache nějaké pole se všemi soubory a ty co budou straší než něco, tak by se odmazaly. (asi by to bylo navěšeno na event onShutdown)
- Honza Marek
- Člen | 1664
Já bych je dal do cache s časovým omezením. Tam je ale zas problém, kdyby někdo chtěl ručně smazat keš, může někomu zrušit právě uploadnuté soubory.
- Honza Kuchař
- Člen | 1662
To ano, ale ukládat do cache přímo binární data které můžou mít třeba 1 GB? To asi nepůjde. :(
- Honza Marek
- Člen | 1664
Jako dohromady 1 GB myslíš? Nevim přesně jak cache funguje a jestli by to teda vadilo…
Každopádně taky místo onShutDown můžeš promazání tempu zavolat před nahráním nového souboru. Vždycky tam pak zůstanou soubory maximálně od jednoho uživatele.
- paranoiq
- Člen | 392
@honzakuchar: co myslíš tím „Uživatel stránku opustí“?
není řešením tohle?:
ignore_user_abort() http://cz2.php.net/…er-abort.php
connection_aborted() http://cz2.php.net/…-aborted.php
Editoval paranoiq (19. 8. 2009 19:04)
- Honza Kuchař
- Člen | 1662
paranoiq: Není, o tom vím. Když odesíláš přes uploadify musíš použít takovou okliku. Každý soubor se odešle vlastním vláknem a potom se nakonec odešle formulář.
Honza M.: Pokud vím, tak do cache můžu uložit cokoliv co jsem serializovat (tzn. string, objekt a tak). Ale já dostanu od klienta soubory. Mám jich 20 a každý z nich má 500MB. Kdybych je chtěl uložit do cache, tak bych je musel celé načíst do paměti a předat cache ne?
Jak se tak dívám, tak to co potřebuji ani není tak úloha pro cache, ale spíš pro nějaké dočasné uložiště. Ještě zkusím něco vymyslet.
Komu nebo čemu vděčím za ten titul? :)
- Honza Marek
- Člen | 1664
My normální smrtelníci umíme přes formulář nahrávat soubor do 2 MB :) Uploadify to snad umí obelstít?
A že by někdo nahrával přes formulář 20 × 500 MB, to bych se divil i kdyby to šlo.
- Honza Kuchař
- Člen | 1662
Všetko záleží na nastavení PHP :)
Přesně tak u našich ve firmě (mají grafické studio) se běžně posílají soubory kole ~200MB. A limit je 1024MB :)
Ale k tomu obejití omezení. Umí to velice elegatně Google Gears. (nezkoušel jsem to, ale mělo by to z principu fungovat) Prostě soubor rozseká na kousky a předá ty kousky JavaScriptu a ten je jeden po druhém odešle. V php se to potom zase slepí dohromady.
- Honza Kuchař
- Člen | 1662
Tak už je krátičká dokumentace a je ke stažení taková jakási první release.
Toto rozšíření je zatím dost experimentální!
JavaScriptová validace: už mám nápad jak na to – bude.
Thread-safe: už mám taky nápad jak na to – bude.
Vytvoření ověřovacího tokenu už při vytváření políčka. Tento token
potom oveřovat u každého odeslaného souboru. Zatím (provizorně) se
vygeneruje náhodné číslo, které se potom pošle přes uplodify a to podle
něho vytvoří / přidá na seznam uploadovaný soubor.
Doufám, že jsem v tom ZIPu na nic nezapomněl.
- Honza Kuchař
- Člen | 1662
Ještě taková ukázka. Exendovaná verze
(MultipleFileUploadEditable
). Veřejný repozitář založím hned
potom co udělám todo.
Editovatelný se myslí, že do toho políčka naházíte soubory, které už máte na server a přidáte sinály na mazání případně na cokoliv dalšího co vás napadne. Výhoda: je to všechno pohoromadě na jednom místě. (i na stránce)
- Honza Kuchař
- Člen | 1662
Ale opravdu zatím moc nedoporučuji nasazení do ostrého provozu! Předpokládám, že se bude vyskytovat asi dost bugů.
- Ondřej Mirtes
- Člen | 1536
Jak můžu oddebugovat, když mi ta komponenta na konci uploadování souboru (těsně před odesláním – reloadnutím stránky) zahlásí HTTP error? Ve firebugu se nic neukáže.
A po odeslání formu mi PHP háže warning, že soubor k přesunutí (někde v tempu) už neexistuje. Takže se prostě neuploaduje úspěšně.
Editoval LastHunter (14. 9. 2009 19:45)
- Honza Kuchař
- Člen | 1662
Mno na debugování na straně serveru xdebug. Na debugování na straně klienta (používá to flash) nic nemám.
Příklad přímo na webu ti funguje?
A po odeslání formu mi PHP háže warning, že soubor k přesunutí (někde v tempu) už neexistuje. Takže se prostě neuploaduje úspěšně.
Používáš poslední revizi Nette? (je vyžadováno Nette d52a529ab0e68a684ce7d0434a97b4165f2ba98b nebo vyšší)
- Ondřej Mirtes
- Člen | 1536
Příklad na webu funguje, revizi Nette jsem dnes kvůli tomu updatoval.
Tak to asi může být leda ve špatném pořadí skriptů/něčemu chybějícímu v hlavičce. Mám tam tohle:
<script type="text/javascript" src="{$baseUri}js/jquery.js"></script>
<script type="text/javascript" src="{$baseUri}js/jq-ui.js"></script>
<script type="text/javascript" src="{$baseUri}js/my.js"></script> <!-- obsahuje nette.ajax -->
<script type="text/JavaScript" src="{$baseUri}js/nette-ajax-form.js"></script>
<script type="text/JavaScript" src="{$baseUri}js/jquery.livequery.js"></script>
<script type="text/JavaScript" src="{$baseUri}js/swfobject.js"></script>
<script type="text/JavaScript" src="{$baseUri}js/jquery.uploadify.js"></script>
<script type="text/JavaScript" src="{$baseUri}js/MultipleFileUpload.js"></script>
A dle Firebugu se všechny tyhle soubory i načtou.
Problém možná bude v nějaké odpovědi od serveru, když je to HTTP error.
Továrnička a obsluhující událost:
protected function createComponentMassImportForm() {
$form = new BaseForm;
$form->getElementPrototype()->class[] = "ajax";
$form->addSelect('gallery', 'Gallery', $this['galleryModel']->getGalleries()->orderBy('time', 'DESC')->fetchPairs('id', 'name'))
->addRule(Form::FILLED, 'Select the gallery!')
->skipFirst('Select the gallery');
$form->addMultipleFileUpload("images","Image upload",20)
->addRule("MultipleFileUpload::validateFilled","Musíte odeslat alespoň jeden soubor!");
$form->onSubmit[] = array($this, 'massImportFormSubmitted');
$form->addSubmit('okSubmit', 'Send');
return $form;
}
public function massImportFormSubmitted(BaseForm $form) {
try {
$values = $form->getValues();
$this['galleryModel']->massImport($values['gallery'], $values['images']);
$this->flashMessage('Photos successfully imported!');
//$this->redirect('Gallery:');
} catch(IOException $e) {
$form->addError($e->getMessage());
}
}
Metoda modelu:
public function massImport($gallery, $images) {
foreach($images as $image) {
if (!$image->getError() == UPLOAD_ERR_NO_FILE && !$image->isOk())
throw new IOException('Error occured while uploading the file.');
if ($image->getContentType() != 'image/jpeg' && $image->getContentType() != 'image/pjpeg' || $image->getContentType() != 'image/jpg') {
throw new IOException('Unsupported image type.');
}
if ($image->isOk()) {
$galleryName = $this->db->fetchSingle('SELECT name FROM [::'. self::GALLERIES_TABLE . '] WHERE id=%i', $gallery);
$this->db->query('INSERT INTO [::'. self::PHOTOS_TABLE . ']', array(
'gallery_id' => $gallery,
'title' => $galleryName,
'description' => NULL,
));
$image->move($this->getImagePath($this->db->insertId()));
}
}
}
Editoval LastHunter (15. 9. 2009 0:13)
- Ondřej Mirtes
- Člen | 1536
Když vypnu v prohlížeči javascript, tak se mi nahoře na stránce objeví tento warning:
Warning: rename(C:\Windows\Temp\php76B0.tmp,D:\Webs\html\Baywatch\www\admin/../../app/temp/MultipleFileUpload-uploads/300174aaec0140eb33-4aaec020db918) [function.rename]: No error in D:\Webs\html\Baywatch\libs\Nette\Web\HttpUploadedFile.php on line 181
A ve formuláři error ‚Unsupported image type‘ – zjevně selhává
$image->getContentType()
.
Editoval LastHunter (15. 9. 2009 0:15)
- Honza Kuchař
- Člen | 1662
Tip 1
Máš tam upravenou verzi nette-ajax-form.js? S tou původní se to chovalo tak všelijak. Je to plně zpětně kompatibilní
Tip 2
(warning) Aaah, už to vidím. Windows neumí přesouvat soubory mezi disky.
(používá se rename) Tempy máš na C: a data na D:
Ale stále mi není jasný, proč je na přesunutí nepoužilo
move_uploaded_file – které evidentně funguje i mezi disky. Protože u mě
to jede suprově. Ale i přes to všechno zkus v HttpUploadedFile změnit
metodu move:
/**
* Move uploaded file to new location.
* @param string
* @return bool
*/
public function move($dest)
{
if (move_uploaded_file($this->tmpName, $dest)) {
$this->tmpName = $dest;
return TRUE;
} else {
if(rename($this->tmpName, $dest)) {
$this->tmpName = $dest;
return TRUE;
} else {
if(copy($this->tmpName, $dest)){
unlink($this->tmpName);
$this->tmpName = $dest;
return TRUE;
}
return FALSE;
}
}
}
Dotaz
Když si z SVN stáhneš ten příklad, tak ti to funguje jak má? Soubory se přesouvají do APP_DIR/uploadData/.
Editoval honzakuchar (15. 9. 2009 11:19)
- Honza Kuchař
- Člen | 1662
Tak s tím content-type. Nette se tak chová i se standardním file
inputem. Mime-typ co mu pošle klient zahazuje a potom se ho snaží zjistit
samo. Nejdříve se pokusí k souboru chovat jako k obrázku. Pokud selže,
pokusí se použít fileinfo
popřípadě
mime_content_type
, která pokud vím, tak na Win není dostupná.
Nicméně mě tedy u obrázků mime-typ určil správně.
Tip: Nemusíš kontrolovat, zda byl soubor v pořádku odeslán – o to se stará už MultipleFileUpload
Zaktualizoval jsem příklad, takže ukazuje co Nette vykouzlilo za Mime-typ
- Ondřej Mirtes
- Člen | 1536
Tak jo, přepsal jsem metodu move
(nestálo by to za
přidání do Nette?), takže při klasickém uploadu
(s vypnutým Javascriptem) to už proběhne v pohodě. Ale při zapnutém JS a
použitém Flashi to vyhazuje stále HTTP Error. Jdu to zkusit na ostrém
serveru.
Mimochodem, jak dostanu do windows open dialogu ty checkboxy, které jsou vyobrazené na screenshotech? Nebo to s touto komponentou nesouvisí a je to nějaké tvé rozšíření winshellu?
Ty jo, tak to začalo fungovat, foto se přidá, i když mi ten flash zahlásí HTTP Error… fakt divné.
Na serveru to bez JS funguje, s JS to neprojde přes podmínku:
->addRule("MultipleFileUpload::validateFilled","You have to upload at least one file!");
Editoval LastHunter (15. 9. 2009 12:54)
- Honza Kuchař
- Člen | 1662
Zkus si prosím stáhnout poslední revizi se SVN. všiml jsem si tam jedné takové nepříjmené krpy. Byla vytvářena AbortException, ale tahle část byla volána mimo Application. → 500 Přepsáno na die a už je vše ok. Alespoň u mě.
- Honza Kuchař
- Člen | 1662
Tak jo, přepsal jsem metodu move (nestálo by to za přidání do Nette?)
No stálo, původně ta metoda šla přesouvat pouze jednou. Viz https://forum.nette.org/…2x-presunout . Jak se tak dívám, tak úplně tu stejnou chybu dostal phx.
- Honza Kuchař
- Člen | 1662
jak dostanu do windows open dialogu ty checkboxy, které jsou vyobrazené na screenshotech
já ti sám nevím, objevilo se mi to tam po tom, co jsem nainstaloval tablet. (asi si to myslí, že je to tablet PC)
- Ondřej Mirtes
- Člen | 1536
Updatnul jsem – na localhostu to už jede, ale na serveru stále před reloadem stránky na konci uploadování blikne HTTP Error a pak to zahlásí, že musím nahrát aspoň jeden soubor :(
- Honza Kuchař
- Člen | 1662
Tak to bude asi nějaký Windows / Linux wtf.
A na čem jsi to testoval?
K tomu HttpErroru. Zkusil bych dočasně oddělat nette-ajax-form (form se neodešlě ajaxově) a ještě v MultipleFileUpload.js odělat ten řádek, kde se znova odesílá formulář. Potom bych se pokusil navěsit na uploadify eventy onError a onComplete a z complete si někam vypsat respose a onError ten errorObj.info. Uvidíš co ti to řekne. Potom bych ještě prozkoumal co se ti objevilo v App_dir/log.
- Honza Kuchař
- Člen | 1662
Ještě bych potom zkusil na řádku 108,109 zakometovat
// V PHP 5.3 zhodí Apache!
if(self::isRequestFromFlash())
Debug::enable(Debug::PRODUCTION);
Editoval honzakuchar (15. 9. 2009 14:22)
- Honza Kuchař
- Člen | 1662
Jenom ještě k tomu Linuxu: testování na jiných platformách než Win je na vás. Mám všechny servery na Windows. Pokud tedy někdo bude mít zájem vyřešit problém na Linuxu, tak ať mě kontaktuje na ICQ (je v profilu tady na fóru). Řešit to tady přímo přes fórum mi přijde dost pomalé.
- Honza Kuchař
- Člen | 1662
Bohužel, bude to chtít krokovat. U mě se všechno chová korektně. (WinXP a Win2k server + PHP 5.2.9) Ale myslím si, že bude někde špatně velikost písmen.
Jenom znova upozorňuji, tato komponenta zatím není vhodná pro produkční nasazení. Chce to to ještě dost věcí doladit. (hlavně ověřovací tokeny a validaci už při uploadu)
- Honza Kuchař
- Člen | 1662
Vidim to o dalších prázdninách.
Proč to ale nejde na Linuxu stále nevím…
Editoval honzakuchar (2. 11. 2009 10:21)
- Panda
- Člen | 569
Tak jsem se v tom trošku vrtal, ale zatím jsem to nijak výrazně nezkoušel. Opravil bych toto:
115: mkdir($dir, 0777);
// práva musí být v osmičkové soustavě, jinak se tam nastavuje 1411 (+t,u+r,g+x,o+x)
447: $template->backLink = (string) $this->form->action;
// explicitní přetypování na string
Po těchto úpravách mi HTTP error na linuxovém serveru zmizel.
- Panda
- Člen | 569
Vyki napsal(a):
Tak bohužel, mě tato úprava nepomohla. Možná je to stupidní dotaz, ale myslel jsem, ze při zapnutém safe mode nefunguje v php funkce mkdir??
mkdir funguje, ale často je nepoužitelný – nově vytvořená složka
má většinou jiného vlastníka, než je vlastník skriptu a tak
safe_mode
nepovolí přístup. Zkus se podívat, co Ti aplikace
píše do error logu ve složce app/log
. Pokud není
v produkčním prostředí, tak bude možná potřeba logování zapnout
pomocí Environment::setMode(Environment::PRODUCTION);
v
bootstrap.php
(log se vytváří jen v produkčním
prostředí).
- Vyki
- Člen | 388
Vyřešeno! Problém je s právy u souboru temp/MultipleFileUpload-uploads. Přes ftp klienta stačí tento adresář, který má špatně nastavená práva smazat a vytvořit jej znova s právy 0777. Potom už vše běží. Když jsem adresář nejdříve nesmazal a snažil se u něj nastavit jiná práva tak to nešlo. Musí se opravdu smazat a znovu vytvořit. + úpravy, které zde napsal Panda
Editoval Vyki (16. 11. 2009 15:35)
- Honza Kuchař
- Člen | 1662
Díky, za vyřešení problému. Je to na SVN.
Ad: $this->form->action;
to je snad string vždy ne?
- Panda
- Člen | 569
honzakuchar napsal(a):
Ad:
$this->form->action;
to je snad string vždy ne?
Ne, AppForm
si tam dává objekt Link
: https://api.nette.org/…orm.php.html#75.
Vyki napsal(a):
Musí se opravdu smazat a znovu vytvořit. + úpravy, které zde napsal Panda.
Mělo by stačit jen smazat, vytvořit se správnými právy by se měl už sám. Na FTP Ti to upravit nešlo právě kvůli tomu, že se prvotně adresář vytvořil se špatnými právy (nešel modifikovat), ale jako objekt nadřazeného adresáře se správnými právy smazat šel.
Editoval Panda (16. 11. 2009 16:51)
- Honza Kuchař
- Člen | 1662
@Panda: Díky opraveno.
@Vyki: Opraveno: pokud nejde zapisovat do vytvořené složky, je použita přímo složka %tempDir%.
Problémy s IE:
- flash ve <form>u je problém. IE si to přeloží jako
window.id.fce(..);
. Ale muselo by to býtwindow.form[x].id.fce(...)
. Řešení: pužívat patchlýswfobject.js
(je v distribuci) Ten objekt flashe „zkopíruje“ do objektuwindow
. (do toho co si vygenruje nejde zasahovat :( ) - flash s id, které obsahuje „-“ je taky problém. IE totiž potom
používá někde interně
id.nejakafce(...)
. Tzn. pak se to přeloží na něco typufrmform-test-text.fce(...)
. Tzn. odečítá od objektufrmform
test
a následnětext
. Ani jedno není definováno. → chyba → absolutně nepoužitelné. Řešení: používat patchnutýjquery.uploadify.v2.1.0.js
, který „-“ nahrazuje. - Je to hrozně pomalé, ale funguje to. Opravdu se vyplatí používat jen u velkých souborů. Mezi soubory čeká. Při výběru čeká. Řešení: používat jakýkoli jiný moderní prohlížeč.
TODO:
- Přestat používat cache jako storage dat.
- Začít používat model, který bude thread-safe
- Modulární přeuspořádní. (že by nenbylo závislé jen na uploadify, ale podporovalo by třeba Google Gears)
- Průběžná validace dat. Aktuálně se všechny soubory přijmou (do tempů) a až poté se zkontrolují. Zatím nevím, jak by to mělo fungovat. Nebo je současné řešení vyhovující? (bezpečnostní mezera tím nevzniká, chová se to stejně jako PHPko, to taky soubory nejdříve všechny přijme, uloží do tempů a až potom je třeba smažem…)
Editoval honzakuchar (17. 11. 2009 14:13)
- Matúš Matula
- Člen | 257
Ahoj,
skusil som implementovat tento addon, na localhoste [Win] fachci. Akonahle ho dam na ostry server [Linux], tak to zrejme pada na tom, ze sa neodosle hlavicka Content-Type ..
MultipleFileUpload.php riadok 110
<?php
if(!$req->getMethod() === "POST" OR !stristr($req->getHeader("Content-type"),"multipart/form-data"))
return;
?>
ked dumpnem $req->getHeader("Content-type")
po odoslani
formulara, tak na locale vypise daco taketo
multipart/form-data; boundary=---------------------------281123069513169
,
na ostrom serveri je to NULL.
Vie mi s tym, prosim, niekto poradit?
- Matúš Matula
- Člen | 257
ano, prava su OK. pouzil som ukazku z https://componette.org/search/?…
. na inom ostrom serveri mi to ide, na danom, na kt. momentalne hostuje projekt,
to vsak nejde a pada
to na vyssie spomenutej podmienke
s hlavickou.
- Honza Kuchař
- Člen | 1662
ked dumpnem $req->getHeader(„Content-type“) po odoslani formulara, tak na locale vypise daco taketo multipart/form-data; boundary=---------------------------281123069513169, na ostrom serveri je to NULL. Vie mi s tym, prosim, niekto poradit?
Tak to by mě také zajímalo. Zkus dumpnout všechny hlavičky, které se poslaly na server. Asi tam budu muset identifikovat ten požadavek nějak jinak.