Forms\ FileUpload: více souborů, multiple a to včetně getValue() – téměř hotovo

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

Ahoj, mám nápad na vylepšení podpory <input type=file multiple>.

  • Změna: metoda addFile a konstruktor FileUpload má další bool parametr.
  • Je-li true, pak přidá atribut multiple=„multiple“ a jméno prvku ve tvaru file[].
  • příslušná hodnota formulářového prvku je pole s HttpUploadedFile (samozřejmě pokud bylo v konstruktoru true)
  • vyřešeny validátory validate_***, ale ještě ne isfilled.
  • Gist

Otestováno

Způsob testu:

  • 1 pole s multiple – nahrány 2 soubory
  • 1 pole s multiple – nahrány další 2 soubory (kontrola promíchání)
  • 1 pole s multiple – nahrán další 1 soubor (kontrolola, zda jde o pole a ne o HttpUplFile)
  • 1 pole bez multiple – nahrán další 1 soubor (kontrola zachování funkčnosti)

Prohlížeče (aneb za to nemůžu)

  • Firefoxu 3.6 – 100% funkčnost
  • Safari 5 Win – 100% funkčnost
  • Chrome (dnešní) – 100% funkčnost
  • Opera verze 11.10 (build 2005, 17.2.2011) a vyšší OK 100%
  • Opera do verze 9,10,11.01 funguje, ale spotřebuje se navíc asi 4× víc RAM(což ale nezpůsobila moje úprava!), než je dat –podrobnosti v dalších postech.
  • IE: 4% V dialogu se mi nepovedlo vybrat víc souborů, ale jinak to funguje OK.

Pak stačí přidat do FormContainer

function addFileMulti($name, $label = NULL){
		$control = new FileUploadMulti($label);
		return $this[$name] = $control;
	}

Příklad výstupních hodnot

Nette\ArrayHash(3) ▼ {
   "multi" => array(2) ▼ [
      0 => Nette\Web\HttpUploadedFile(5) ▼ {
         "name" private => "Windows7.full.iso" (13)
         "type" private => NULL
         "size" private => 208922827
         "tmpName" private => "C:\web\php\tmp\php5579.tmp" (26)
         "error" private => 0
      }
      1 => Nette\Web\HttpUploadedFile(5) ▼ {
         "name" ...
      }
   ]
   "multi_vybran_jeden_soubor_" => array(1) ▼ [
      0 => Nette\Web\HttpUploadedFile(5) ▼ {
         "name" ...
      }
   ]
   "single" => Nette\Web\HttpUploadedFile(5) ▼ {
      "name" ...
   }
}

K diskuzi je

  • případně mi poradit, proč v IE to zase nejde
  • zvážit parametry min a max
  • způsob validace (Image, MaxSize, Mime) (hardcoded nebo nastavitelné?)
    • pokud jen jeden soubor nevyhovuje, pak se vyhodí error (a data jsou zahozena)
    • nevyhovující soubory jsou (tiše) vyjmuty

Editoval bojovyletoun (14. 3. 2011 23:32)

Filip Procházka
Moderator | 4668
+
0
-

git si rozhodně nainstaluj :)

Stručný návod jak přispívat:
1) registrace na github → fork na https://github.com/nette/nette

# stáhnout nette
$ git clone git://github.com/bojovyletoun/nette.git

# vytvoření nové větve
$ git checkout -b feature-multiple-file-upload master

provedeš úpravy..

# uložit všechny změny
$ git commit -a -m "Multiple file upload"

# nahrát na github
$ git push origin feature-multiple-file-upload

na githubu vlevo pod Source si klikneš na svoji větev feature-multiple-file-upload a vpravo nahoře Pull Request a budeš doufat :)

Patrik Votoček
Člen | 2221
+
0
-

kdo ví třeba to je to v nových formulářích už obsaženo…

bojovyletoun
Člen | 667
+
0
-

OK, dal jsem pull. Jen mě trápí, jak sloučit 2 commity (přes rozhraní githubu)). Editoval jsem to přes web, takže tam mám commit pro Formcontainer.php a commit pro Fileupload.php

Editoval bojovyletoun (11. 3. 2011 0:13)

Honza Marek
Člen | 1664
+
0
-

A co když na formulářový políčko nastavíš validaci podle mime typu třeba? To bude taky asi potřeba upravit, ne? Pak to užitečné beze sporu bude.

bojovyletoun
Člen | 667
+
0
-

Tak jsem validaci přidal tak, že validační funkce jsem prefixoval _ a funkce callStatic jim podsune Objekt FileUpload s upravenou hodnotou value… Pak to běží přes foreach, dosadí se zpátky původní pole souborů a testuje se výsledné pole na výskyt false..

Jen jsem ještě neošetřil isFilled.

Dál, je možné nějak spojit commity do jednoho? Když nedám pull request, jakým příkazem se t dá do hlavní větve.
zdroj FileUpload, zdroj FOrmcontainer

jtousek
Člen | 951
+
0
-

Hezká práce. :)

Popravdě ale raději soubory používám jinak. Když už totiž nějaký soubory nahaješ (i třeba dříve) tak u normálního fileuploadu není možnost je smazat, což uživatelé potřebují. Proto jsem upload souborů přesunul mimo formuláře a řeším to tímhle. Výhodou je drag&drop upload ve FF a Chromu.

Ondřej Mirtes
Člen | 1536
+
0
-

Spojit více commitů do jednoho můžeš na svém počítači pomocí git rebase a na GitHub je promítnout do té samé větve pomocí git push --force.

http://gitready.com/…-rebase.html

Filip Procházka
Moderator | 4668
+
0
-

On se evidentně snaží vyhnout tomu, aby to musel stahovat na svůj pc :)

bojovyletoun
Člen | 667
+
0
-

tak u normálního fileuploadu není možnost je smazat, což uživatelé potřebují. Proto jsem upload souborů přesunul mimo formuláře

Na valums uploader jsem se díval, je to pěkná věcička. Jenže myslím, že rozdíl je tam vidět: valums je nahrávač souborů se vším všudy (progress,javascript,komponenta) a tohle je čistě (lowlevel) rozšíření. Pokud to v prohlížeči nefunguje, tak je možnost si tam ručně přidat skriptík, který po přidání souboru přidá další input – na serveru se to interpretuje správně.

Ještě chci vyřešit věci: (kód v 1. příspěvku – odkaz na gisthub)

  • možná přesun do vlastní třídy -HOTOVO
  • dodělat validaci hotovo (neumí min, max)
  • pokusit se o podporu pro Opery (pomocí tmpfile() a new HttpUploadFile)

Editoval bojovyletoun (11. 3. 2011 15:04)

bojovyletoun
Člen | 667
+
0
-

Takže už to funguje i v prohlížečích, které posílají multipart/mixed, což je Opera(9,10,11, kromě 11.50).

Dobré zprávy (multipart/mixed – Opera)

  • soubory se tvoří přes tempnam() a pracuje se s nimi jako s ostatními nahranými soubory, soubory jsou automaticky mazány přes register_shutdown_function – funguje OK, i když nastane chyba skriptu
  • data jsou čteny přes $_POST, takže binární data zůstanou nezměněna. PS:Netestováno na Linuxu. Také nevím, co se stane se zapnutými gpc
  • moje úprava má zanedbatelnou spotřebu prostředků

"Špatné zprávy (multipart/mixed – Opera)

dochází k obrovské spotřebě prostředků:

  • Jen samotné nahrání na server, bez načtení frameworku
    • CPU: nevím jak zjistit, tipuji 1MB dat → 50ms
    • RAM: 1MB dat → 3MB RAM
  • frameworkem
    • CPU: 1MB dat → 100ms
    • RAM: 1MB dat → 4MB RAM

Tedy jen samotné nahrání na server je problém, čisté PHP si vzalo pro 7MB dat 22MB paměti.

Editoval bojovyletoun (13. 3. 2011 13:02)

jtousek
Člen | 951
+
0
-

A tento problém je pouze s Operou a multipart/mixed? Není to divný?

bojovyletoun
Člen | 667
+
0
-

myslíš spotřebu paměti? Je to obecně problém posílání velkého množství dat přes POST . U normálních prohlížečů se to taky samozřejmě posílá přes POST(ale multipart/formdata), jenže nějaký filtr(možná už i webserver?) předtím na vstupu rozpozná multipart/formdata a „přesune“ je do $_FILES, takže pak neprochází nějakými dalšími filtry(které tak žerou paměť). No a multipart/mixed nenajde, takže zůstanou v $_POST.

V Opeře 11.50 už posílá normálně. Když vyberu hodně dat (nad 10MB – podmínka, je že musí být v jednom inputu vybrány aspoň 2 soubory), tak stránka prostě nereaguje po odešlání formu.

Otázka je, zda to nechat jako experimentální. Ostatně stačí si zakomentovat 2 řádky, a bude se to chovat, jako by byl soubor nenahrál.

OT: na myego se píše o Ajaxovém nahrávání obrázků. Myslí se tím XMLHttpRequest 2? Ten totiž už prý umí nahrávat soubory. Nebo je tím myšlen iframe nabo snad flash?

Editoval bojovyletoun (13. 3. 2011 13:22)

jtousek
Člen | 951
+
0
-

Firefox a Chrome umí posílat soubory přes xmlhttprequest, Opera ne.

Btw. Opera (starší než 11.50) si nebere mime-type z form action? Nějak nechápu odkud bere to multipart/mixed. Nešlo by Operu přinutit aby použila co má?

Nakonec na tom ale až tolik nezáleží. Uživatelé opery většinou rychle aktualizují takže tolik nevadí, že to funguje jen v nejnovější.

bojovyletoun
Člen | 667
+
0
-

Zkoušel jsem FF a chrome, přes ajax to nejde. Možná je potřeba použít něco jako XMLHttpRequest2(), ale dnes nemám náladu na javascript.

Ve zdrojáku je enctype="multipart/form-data", ale je to k ničemu.

S těmi aktualizacemi je to pravda (zvlášť u Opery) a navíc je to okrajový problém a Opera má fakt málo lidí.

Ještě malý benchmark: 30MB souborů: Opera: 3600ms, 160MB, v jiných prohlížečích standartní.

Jo ještě chci dodat, že je vždy možnost(hlavně pro IE) si dopsat skriptík, který třeba na onchange za aktuální input připojí nový a soubory naklikat po jednom. V opeře se to pak chová normálně.

Nehodí se to spíš do extras?

Pak bych měl ještě otázku: Co považujete za lepší v případě, že má prvek pravidlo (např MAXsize):

  • klasicky: pokud je jeden přesáhne velikost, přidá se chyba (a data se zahodí)
  • ty soubory, které budu větší se (tiše) vyfitlrují

Editoval bojovyletoun (14. 3. 2011 22:57)