O webu
Drag & Drop upload obrázků

Pro maximální pohodlí návštěvníka při nahrávání obrázků na web existuje řada postupů, které fungují v různých prohlížečích.

Ukázka maximálních možností uploadu běží na: img.djpw.cz

Vložení ze schránky

Příjemná funkce je vložení obrázku ze schránky. Zde se situace lehce komplikuje, neboť mohou nastat dva případy vložení.

  1. Uživatel zkopíruje celý soubor. Buď přes kontextové menu, nebo například klávesovou zkratkou Ctrl + C.

    Kopírování souboru do schránky

  2. Uživatel zkopíruje kus (výřez) obrázku do schránky. To může nastat v grafickém editoru, také po vyfocení obrazovky klávesou PrintScreen (či jen aktivního okna – Alt + PrintScreen) nebo třeba v prohlížeči po vybrání volby Kopírovat obrázek z kontextového menu.
  3. Kopírování obrázku do schránky

Využití contenteditable

Ve Firefoxu a IE 11 je možné využít toho, že tyto prohlížeče umí (například) screenshot obrazovky vložit jako obrázek do elementu s contenteditable/designMode (tak si je možné vytvořit i vlastní WYSIWYG edtior).

Firefox takto umí dokonce i vkládat zkopírované soubory.

Živá ukázka – vložení obrázku ze schránky do contenteditable

Jinde to ale nefunguje. Ve staré Opeře 12 a starších IE není možnost obrázek ze schránky vložit (jen s využitím HTML a JS) v jakékoliv podobě bez použití nějakých pluginů v Javě nebo Flashi.

V Chrome jde použít clipboardData.

Živá ukázka – získání souboru z clipboardData

Drag & Drop souborů

Kromě vložení přes Ctrl + V se může hodit i nahrání souboru jeho přetažením na stránku.

V Chrome, nové Opeře a Firefoxu jde soubor přesunout přímo na <input type="file"> bez nutnosti dalšího programování. Internet Explorer ani MS Edge toto neumí.

Jelikož je ale dobré mít uploadování funkční i v IE a navíc se hodí mít pro přesun souborů vyhrazenou větší plochu, která bude po přetažení navíc signalisovat možnost tažený soubor upustit, je řešení pomocí <input type="file"> celkem nedostatečné.

Nejdříve je nutné zablokovat výchozí akci prohlížeče při přetáhnutí souboru, což někde způsobí nežádoucí přechod na obrázek. Slouží k tomu drag & drop událostí:

function prevent(e) {
    e = e || event;
    e.preventDefault();
}
window.addEventListener("dragover", prevent, false);
window.addEventListener("drop", prevent, false);

Přetažený soubor (nebo soubory) se získají z event.dataTransfer.files při události ondrop (upuštění obrázku).

Živá ukázka – výpis přetažených souborů

Zvýraznění při přesouvání

Aby uživatel poznal, že stránka na přesouvání souboru reaguje, použijí se příslušné drag & drop události myši.

Zvýraznění drag and drop

  • ondragenter – uživatel najel se souborem na oblast (provést zvýraznění)
  • ondragleave – uživatel najel se souborem na oblast (zrušit zvýraznění)
  • ondrop – uživatel soubor v dané oblasti pustil

Zobrazení souboru

Když člověk vybere soubor (je jedno, jestli přesunutím na <input type="file"> nebo po kliknutí na tlačítko Vybrat) či použije přetažení myší, je dobré obsah obrázku ihned zobrazit.

Kromě toho, že návštěvník získá jistotu, že vybral správný obrázek a uvidí ho prakticky ihned, lze nabídnout i nějaké základní funkce pro práci s obrázky:

  • změnu velikosti,
  • oříznutí,
  • otočení/převrácení

To lze v dnešních prohlížečích řešit na straně klienta bez nutnosti přenášet data na server. Zvlášť výhodné je to u zmenšování obrázků, což je výborné i pro uživatele, neboť nemusí třeba X megabytový soubor vůbec nikam přenášet, ale nahraje se jen jeho zmenšenina.

Slouží k tomu FileReader:

FileReader

Řešení pomocí FileReader funguje od IE 10. Pro vložení souboru jako obrázku na stránku je potřeba z původního souboru udělat data URL, která se nastaví jako src pro <img> (popř. se následně tento obrázek může nakreslit do <canvas>u). Převod na data URL řeší metoda readAsDataURL, které se jako argument předá soubor získaný z políčka pro nahrávání souborů nebo pomocí dataTransfer při přesunutí souboru do stránky pomocí drag&dropu.

FileReader se používá velmi jednoduše:

var reader = new FileReader();
reader.onload = function (e) {
  obrazek.src = e.target.result;
};
reader.readAsDataURL(soubor);
  • Proměnná obrazek je <img> značka na stránce, kde se obrázek objeví.
  • Proměnná soubor obsahuje soubor získaný přes dataTransfer.files / input.files.

Za povšimnutí stojí konstrukce reader.onload, která se spustí po dokončení readAsDataURL.

Zobrazení přetaženého souboru

Hotové řešení zobrazení obrázku po drag & drop může vypadat následovně:

Živá ukázka – zobrazení obrázku před uploadem

Zobrazení souboru z <input>u

V případě políčka <input> se celý proces náhledu nabízí volat při změně (onchange) nahrávacího <input>u.

<input type="file" onchange="zobrazit(this)">

Funkci zobrazit se předává nahrávací políčko (this) právě kvůli následnému získání souboru z input.files.

Živá ukázka – okamžitého zobrazení obrázku z <input type="file">

Více souborů najednou

Pro upload více souborů existuje HTML atribut multiple. V takovém případě bude záhodno zobrazit náhledy všech souborů. Počet souborů se zjistí z input.files.length. Potom stačí soubory projít běžným cyklem for a místo nuly na místě indexu použít index každého jednoho souboru.

Živá ukázka – zobrazení více souboru z multiple upload políčka.

Zobrazení více (datově větších) souborů zabere nějaký čas. Tudíž by bylo dobré dát uživateli vědět, že se něco děje.

Kreslení do <canvas>u

Překreslit obrázek do elementu <canvas> jde metodou drawImage. Nejdříve se v HTML kódu připraví plátno (<canvas>).

<canvas id="cc"></canvas>

Do pomocných proměnných se přidá odkaz na <canvas> a jeho 2D kontext.

var canvas = document.getElementById("cc");
var ctx = canvas.getContext("2d");

A do funkce prováděné při reader.onload se pár řádky nakreslí na plátno obrázek. Může se hodit i nastavit <canvas>u rozměry podle obrázku.

canvas.width = obrazek.width;
canvas.height = obrazek.height;
ctx.drawImage(obrazek, 0, 0);

Druhý a třetí parametr funkce drawImage určuje souřadnice, kam se má obrázek nakreslit – cílem je ho umístit hned do levého horního rohu, proto ty dvě nuly. Proměnná obrazek je <img> element.

Živá ukázka – překreslení obrázku do <canvas>u

Kvůli IE musí obrázek na stránce skutečně existovat (být přidán do DOMu), byť třeba skrytý.

var obrazek = new Image();
obrazek.src = e.target.result;

Tato elegantnější konstrukce proto nebude v IE fungovat (ukázka).

Zobrazení textu před uploadem

Také textový soubor je možné před samotným nahráváním zobrazit.

Místo metody readAsDataURL k tomu slouží přečtení souboru jako textreadAsText a výsledek se nastaví jako innerHTML nějakého elementu.

Živá ukázka

Starší prohlížeče

V IE 9 a starších není možné použít FileReader. Pro zobrazení náhledu tak nezbývá než soubor nahrát na server a až ten zobrazit. Případně použít nějaký plugin ve Flashi nebo Javě.

Údajně by ve starších IE mohlo fungovat tohleukázka – v IE 9 to ale funkčně nevypadá.

Upload souboru

Samotný proces nahrání souboru na server proběhne například odesláním AJAXového požadavku. Jako data se odešle data URL.

Obsah <canvas>u se získá prostým canvas.toDataURL().

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    alert(xhr.responseText); // výstup PHP skriptu
  }
}
xhr.open("POST", "upload.php");
xhr.setRequestHeader(
  "Content-type", 
  "application/x-www-form-urlencoded"
);
xhr.send("data=" + canvas.toDataURL());

PHP skript upload.php, který tento obsah uložit do souboru může vypadat takto:

if (isset($_POST["data"])) {
  $img = $_POST["data"];
  $img = str_replace('data:image/png;base64,', '', $img);
  $img = str_replace(' ', '+', $img);
  $data = base64_decode($img);
  $filename = time() . ".png";
  file_put_contents($filename, $data);
  echo $filename;
}
else {
  echo "Error";
}
  • Při ukládání souborů je v praxi dobré zajistit, aby nemohlo vzniknout hodně souborů v rámci jedné složky.

  • Taktéž určování názvu souboru podle timestampu (času požadavku na uložení) nebude v případě používání uploadu více uživateli ideální. Mohli by si navzájem obrázky přepisovat.

  • Lepší postup je o obrázcích ukládat záznam v databási a fysický obrázek na serveru pojmenovat až na základě identifikátoru v DB (popř. nějakým hashem).

  • Skript natvrdo ukládá obrázky jako PNG. To v případě například fotek nebude s ohledem na datovou velikost optimální formát.

Odkazy jinam