FileUpload a (ne)overwritnutí souboru na serveru
- Ascaria
- Člen | 187
Zdravím,
Bylo by dobré, kdyby třída FileUpload měla možnost přejmenovat svůj soubor, pokud již jiný se stejným názvem ve složce existuje. Například „soubor.jpg“ přejmenuje na „soubor1.jpg“ a tak je pak 100% jistota, že nedojde k nechtěnému přepsání. Viděl jsem to implementováno zde http://www.verot.net/…s_upload.htm a myslím, že je to potřeba.
Ascaria
Editoval Ascaria (20. 3. 2012 16:33)
- Ascaria
- Člen | 187
Mě na tom nepřijde nic magického, například:
<?php
if($fileupload->isOk())
{
$fileupload->overwrite = false; // nebo treba ->autoRename = true;
$fileName = $fileupload->move('/path/'.$fileupload->sanitizedName);
// $fileName ted muze obsahovat 'soubor_8.jpg'
}
?>
Zdá se mi to daleko lepší, než v 10ti metodách, co ukládají nějaké soubory někam psát dokola foreache, které budou kontrolovat existenci ‚soubor‘, ‚soubor1‘, ‚soubor2‘ atd…
Editoval Ascaria (20. 3. 2012 16:51)
- Lopo
- Člen | 277
a ako by si chcel riesit situaciu, ze uz je na serveri napr. subor1 aj subor11 pricom su to uplne odlisne subory a potrebujes nahrat zase subor1 ? subor11 ho nazvat nemozes lebo ten tam uz je … rovnako mozes mat problem ak user posiela viacere rozne subory s rovnakymi nazvami
ja som to napr. riesil v 1 projekte pomenovanim suboru zlozenym z 3 casti – original nazov od usera + hash celej davky uploadu + hash pre subor v ramci davky – pritom generovanie hash kodov bolo skladane tak aby bola zaistena unikatnost (nastastie upload bol strojovo a len 1× denne), pri castejsom uploade by sa asi muselo zohladnit viacero zdrojov unikatnosti (userid, cas, velkost suboru, md5 suboru a pod.)
dalsie moznosti su cislovanie suborov zaradom a ukladanie co najviac informacii o nich do DB, alebo vyuzitie nejakej adresarovej struktury, pripadne nejake dalsie moznosti a ich kombinacie
z toho mi vyplyva, ze toto proste musi podla mna riesit programator vzdy pre konkretny pripad na mieru, nemal by to nejako magicky riesit framework … napr. aj kvoli tomu ze to jedno konkretne riesenie nemusi vo vacsine pripadov vyhovovat (niekedy moze byt nedostatocne a inokedy zase zbytocne zlozite)
- Ascaria
- Člen | 187
No moje představa by byla zhruba taková:
<?php
public function move($dst)
{
$counter = 1;
$tmp = bez_pripony($dst);
$ext = pripona($dst);
while(true)
{
if(!is_file($tmp.$ext))
{
// přesunutí souboru
// přenastavení sanitizedName, ale nikoliv name
break;
}
else
{
$tmp = $dst.'_'.($counter++).$ext;
}
}
return sanitizedName;
}
?>
Tímto způsobem by bylo možné nahrat max int stejných souborů, to si
myslim je už dostatečné.
Pak tam třeba dát metodu ve stylu (a další):
<?php
$fileupload->hasSanitizedNameChanged();
?>
Editoval Ascaria (21. 3. 2012 19:37)
- Ascaria
- Člen | 187
gmvasek napsal(a):
@Ascaria: přečti si příspěvek od @Lopo.
No myslel jsem si, že kdyby FileUpload byl schopen sám zajistit unikátnost názvu souboru (když umí zkopírovat soubor a má metody pro detekci obrázku), bylo by to kuprospěchu. Zdá se že máte jiný názor.
Lopo napsal(a):
a ako by si chcel riesit situaciu, ze uz je na serveri napr. subor1 aj subor11 pricom su to uplne odlisne subory a potrebujes nahrat zase subor1 ? subor11 ho nazvat nemozes lebo ten tam uz je … rovnako mozes mat problem ak user posiela viacere rozne subory s rovnakymi nazvami
nahraju soubor.jpg – udělá se z toho soubor.jpg
nahraju soubor.jpg – udělá se z toho soubor__1.jpg
nahraju soubor.jpg – udělá se z toho soubor__2.jpg
nahraju soubor.jpg – udělá se z toho soubor__3.jpg
a pro změnu
nahraju soubor__1.jpg – udělá se z toho soubor__4.jpg anebo
soubor__1__1.jpg
nahraju soubor__1__1.jpg – udělá se z toho soubor__5.jpg anebo
soubor__1__1__1.jpg
nahraju soubor__1.jpg – udělá se z toho soubor__6.jpg anebo
soubor__1__2.jpg
… to už záleží na implementaci jestli bude rozpoznávat counter
and so on
Je jedno jestli to nahrává víc userů, nebo jeden user víckrát stejný název. Podstatné je, že mě nezajímá název uploadnutého souboru jako takový, ale zajímá mě jen název, pod kterým se to uložilo na server (pokud někoho zajímá název souboru, může autorename nezapnout, nebo si název vytáhnout před move(), nebo se přidá metoda movedSanitizedName, možností je hodně). Jestli jsem to tímhle nevysvětlil, tak nechápu tvojí otázku.
Editoval Ascaria (22. 3. 2012 16:44)
- Lopo
- Člen | 277
Ascaria napsal(a):
… Podstatné je, že mě nezajímá název uploadnutého souboru jako takový …
v tom pripade nepotrebujes zadne special algoritmy na pomenovavanie suborov a riesenie konfliktov, proste cislujes vo vzostupnej postupnosti … a tym padom nemas vobec dovod riesit nejake pomenovavanie suborov a riesenie konfliktov … staci ti len mat niekde ulozene to pocitadlo …
a to je zase to co som uz napisal – niekedy dane konkretne riesenie moze byt slabe/nepostacujuce a inokedy zase zbytocne komplikovane, tj treba to vzdy riesit pre konkretny pripad na mieru a preto to nemoze riesit framework
kapisto ?
- Ascaria
- Člen | 187
@22: Tak pak můžu být jedině o dost lepší a nebo o dost horší programátor, než většina populace :)
@Lopo: Nicméně stojím si za tím, že ten counter, jak tomu říkáš (akorád u mě by to byl defacto „anonymní counter“ pro každý jeden název souboru), by byl pro framework velkým přínosem, odpovídá KISS a zajištuje unikátnost názvu lépe (čti 100%), než „název od usera + hash1 + hash2 + zmíněné další zdroje unikátnosti při častějším uploadování, než 1× denně“ nemluvě o přizpůsobování struktury složek (eh). Nezlob se na mě, ale tebou popisované řešení je moc komplikované.
Editoval Ascaria (26. 3. 2012 10:08)
- Lopo
- Člen | 277
Ascaria napsal(a):
@Lopo: Nicméně stojím si za tím, že ten counter, jak tomu říkáš (akorád u mě by to byl defacto „anonymní counter“ pro každý jeden název souboru), by byl pro framework velkým přínosem, odpovídá KISS a zajištuje unikátnost názvu lépe (čti 100%), než „název od usera + hash1 + hash2 + zmíněné další zdroje unikátnosti při častějším uploadování, než 1× denně“ nemluvě o přizpůsobování struktury složek (eh). Nezlob se na mě, ale tebou popisované řešení je moc komplikované.
lenze zabudas ze v pripade obycajneho counteru potrebujes tie udaje o suboroch ulozit niekde inde (najlepsie v DB) co uz zase ten tvoj KISS porusuje (zase len nejakym sposobom sa vnucuje nejaka struktura tych udajov) … a zase mat na serveri niekde v adresari kopec ocislovanych suborov bez akychkolvek informacii je zase k nicomu … a ta adresarova struktura + hashe a pod. bolo len special riesenie ktoreho obdobu som pouzil v ramci jedneho projektu, vobec som nehovoril ze sa ma integrovat do nette tak jako sa snazis pretlacit to svoje riesenie
proste pochop uz konecne ze taketo problemy nemaju univerzalne riesenie, a jedno konkretne riesenie nema moc zmysel integrovat natvrdo pretoze vyhovuje malokomu a tym padom je to len kopec zbytocneho kodu (maximalne sa pokusit nadefinovat nejaky interface, ale jeho implementaciu si kazdy bude moset urobit sam)
tymto v tomto threade koncim akukolvek dalsiu diskusiu pretoze ked si ktosi furt mele len svoje napriek akymkolvek rozumnym argumentom kopy dalsich ludi … tak nema zmysel pokracovat
- Ascaria
- Člen | 187
Tady je má implementace, mám jí v BaseModelu:
/**
* Generates empty unique filename (from absolute path with filename), that can be later safely overwritten (next call of this method will generate another filename) by another script.
* @param String $filename absolute path to file with filename
* @return string
*/
protected function uniqueName($filename)
{
// Načteme si části souboru do proměnné, odebereme counter souboru (pokud ho má, např. "file-5.jpg" => "file.jpg") a nastavíme counter
$counter = 1;
$pathinfo = pathinfo($filename);
$pathinfo['filename'] = preg_replace('/[0-9-]+$/', '', $pathinfo['filename']);
// Pokud nemůžeme do složky zapisovat, vyhodíme výjimku
if(is_dir($pathinfo['dirname']) && !is_writable($pathinfo['dirname']))
throw new \Nette\IOException('Cannot write to directory "'.$pathinfo['dirname'].'"');
// Procházíme, dokud nenarazíme na neexistující soubor, pak se pokusíme vytvořit nový a přerušíme cyklus
while(true)
{
$filename = $pathinfo['dirname'].'/'.strtolower($pathinfo['filename'].'-'.($counter++).'.'.$pathinfo['extension']);
// Vytvoříme soubor, abychom ho "rezervovali" pro skript (další průběh této metody ve stejném čase v jiném vlákně pak již vytvoří název jiný)
if(!file_exists($filename) && false !== file_put_contents($filename, ''))
break;
}
// Vrátíme unikátní název právě vytvořeného prázdného souboru
return $filename;
}
Použití:
$dir = $this->dirs['wwwDir'].$this->dirs['resourcesDir'].'/certificationmodule';
$toFile = $this->uniqueName($dir.'/'.$fileupload->sanitizedName);
Pořád by se mi líbilo, aby to bylo součástí Nette.
Editoval Ascaria (3. 4. 2012 15:23)