FileUpload a (ne)overwritnutí souboru na serveru

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

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)

Aurielle
Člen | 1281
+
0
-

To je moc magické. Nepřepsání souboru si má řešit programátor a ne že na to za něj bude myslet třída.

Ascaria
Člen | 187
+
0
-

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
+
0
-

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
+
0
-

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)

Aurielle
Člen | 1281
+
0
-

Tak si ji implementuj a používej. Obecně je to moc magické, jak jsem už psal.

Ascaria
Člen | 187
+
0
-

Nechci měnit zdroják Nette. Mohl bys prosím vysvětlit, proč je to obecně moc magické? Pořád to totiž nechápu.

Editoval Ascaria (22. 3. 2012 10:50)

OK3
Člen | 91
+
0
-

Měnit ho nemusíš :) Stačí, když podědíš třídu FileUpload a co potřebuješ, to si do/přepíšeš v potomkovi té třídy.

Aurielle
Člen | 1281
+
0
-

@Ascaria: přečti si příspěvek od @Lopo.

Ascaria
Člen | 187
+
0
-

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
+
0
-

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 ?

22
Člen | 1478
+
0
-

@Ascaria: všiml jsem si, že na některé věci jdeš úplně s nějakou jinou logikou, než zbytek populace .-) takže spíš hledej řešení, jak se to dělá a nehledej problém ve frameworku, proč nedělá to, co by podle tvého presvědčení dělat měl.

Editoval 22 (22. 3. 2012 20:13)

Ascaria
Člen | 187
+
0
-

@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
+
0
-

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
+
0
-

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)