Přejít k navigační liště

Zdroják » Různé » Jak navázat na přerušený nebo poškozený upload souboru

Jak navázat na přerušený nebo poškozený upload souboru

Články Různé

Upload souborů přes webové rozhraní není příliš komfortní – strohý dialog pro výběr souborů, donedávna možnost vybrat jeden soubor v dialogu, téměř nulová schopnost interakce – to vše způsobilo, že webdesignéři sahali raději k řešení ve Flashi či Javě. Nové prohlížeče se snaží nabídnout větší komfort – dnešní článek je toho ukázkou.

Autor článku vyzkoušel nové File API a metodu slice(), která byla představena ve Firefoxu 4. V tomto článku popisuje, jak navázat na přerušený upload souboru.

Upload souboru probíhá přes objekt XHR Level2. Ten poskytuje různé metody a události pro zpracování požadavku (například pro odesílání dat a průběžnou kontrolu jejich odesílání) a obsluhu odpovědi (jestli proběhl upload v pořádku, nebo se vyskytla chyba). Více informací naleznete v článku Upload obrázků pomocí HTML5.

XHR objekt naneštěstí nepodporuje metody pro pozastavení a následné navázání uploadu. Tuto funkci je ale možné implementovat kombinací nové metody slice() ve File API s abort()  v XHR. Pojďme se podívat, jak na to.

Autorem článku je Simon Speich — webový vývojář, nadšenec do webových standardů a milovník Mozilly od verze 0.8. Originál vyšel na webu Mozilla Hacks pod licencí CC-BY-SA. Pod stejnou licencí je k dispozici i zde.

Ukázka

Můžete si prohlédnout funkční ukázku fileUploaderu, nebo si stáhnout JavaScriptový a PHP kód z github.com.

Pozastavení a obnovení uploadu

Základní myšlenka je poskytnout uživateli tlačítko k pozastavení průběhu uploadu a pro jeho následné obnovení. Pozastavení je jednoduché — stačí zavolat metodu abort(). Je nutné se ale ujistit, že to uživatelské rozhraní nenahlásí jako chybu.

Obnovení uploadu je mnohem složitější, protože byl požadavek zrušen a spojení se serverem uzavřeno. Namísto opětovného poslání celého souboru použijeme metodu mozSlice() z objektu Blob, která vrátí zbývající část dat. Poté vytvoříme nové spojení se serverem, tuto část odešleme a připojíme na konec již odeslaných dat.

Rozdělení souboru na části

Odesílaný soubor můžeme rozdělit na části následujícím způsobem:

var chunk = file.mozSlice(start, end);

Stačí nám vědět, kolik bajtů jsme již odeslali na server. To je vše. Mohlo by se zdát, že nejjednodušší způsob je uložit si atribut loaded události ProgressEvent před tím, než upload přerušíme. Toto číslo však nemusí souhlasit s počtem bajtů uložených na serveru. Nejjistější způsob je před samotným obnovením uploadu odeslat dotaz, který nám vrátí velikost přerušeného souboru. Tuto informaci pak můžeme využít při dělení dat pomocí  mozSlice().

Souhrn událostí

(předpokládejme, že upload již běží):

  1. uživatel přeruší upload,
  2. změníme stav UI — přerušeno,
  3. upload je přerušen,
  4. server přeruší zápis na disk.
  5. Uživatel obnoví upload,
  6. změníme stav UI — obnoveno,
  7. dotaz na velikost již zapsaných dat na serveru,
  8. rozdělení souboru na zbývající část,
  9. odesílání,
  10. změníme stav UI — odesílání,
  11. server navazuje na přerušený upload.

JavaScriptový kód

// Předpokládejme, že jsme již odeslali dotaz na velikost
// odeslaného souboru a odpověď xhr.result obsahuje počet
// bajtů souboru, které jsou již zapsané na disk.
var start = xhr.result.numWrittenBytes;
var chunk = file.mozSlice(start, file.size);

var req = new XMLHttpRequest();
req.open('post', 'fnc.php?fnc=resume', true);

req.setRequestHeader("Cache-Control", "no-cache");
req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
req.setRequestHeader("X-File-Name", file.name);
req.setRequestHeader("X-File-Size", file.size);

req.send(chunk);

PHP kód

Jediná změna na serverové části je, že při normálním uploadu vytvoříme nový soubor, zatímco při obnovení na soubor navážeme.

$headers = getallheaders();
$protocol = $_SERVER['SERVER_PROTOCOL'];
$fnc = isset($_GET['fnc']) ? $_GET['fnc'] : null;
$file = new stdClass();
$file->name = basename($headers['X-File-Name']));
$file->size = $headers['X-File-Size']);

// php://input obchází nastavení v php.ini, proto
// musíme omezit velikost souboru sami:

$maxUpload = getBytes(ini_get('upload_max_filesize'));
$maxPost = getBytes(ini_get('post_max_size'));
$memoryLimit = getBytes(ini_get('memory_limit'));
$limit = min($maxUpload, $maxPost, $memoryLimit);
if ($headers['Content-Length'] > $limit) {
  header($protocol.' 403 Forbidden');
  exit('File size to big. Limit is '.$limit. ' bytes.');
}

$file->content = file_get_contents(’php://input’);
$flag = ($fnc == ‘resume’ ? FILE_APPEND : 0);
file_put_contents($file->name, $file->content, $flag);

function getBytes($val) {

$val = trim($val);
      $last = strtolower($val[strlen($val) - 1]);
      switch ($last) {
          case 'g': $val *= 1024;
          case 'm': $val *= 1024;
          case 'k': $val *= 1024;
      }

return $val;
}

Pozor!

Výše uvedený PHP kód neobsahuje žádné bezpečnostní kontroly, je to pouhý Proof-of-Concept. Uživatel by s takovým kódem mohl na server odeslat libovolný typ souboru, nebo také libovolný soubor přepsat. Je proto nutné před samotným uploadem provést příslušná bezpečnostní opatření – ovšem ta už jsou mimo rámec tohoto článku.

Obnovení uploadu po chybě

Pozastavení a následné navázání uploadu lze použít i při problémech se sítí. Místo abychom znova odesílali celý soubor, navážeme na odesílání dat výše popsaným způsobem.

Poznámka k obnovení uploadu

Jelikož nemáme kontrolu nad tím, jaká data server uložil do souboru před přerušením uploadu, může se stát, že uložíme soubor poškozený. Toto je potřeba ošetřit. Nabízí se metody kontrolního součtu či jiný způsob ověření.

Obnovení uploadu po pádu prohlížeče

Výše popsaný postup se dá dokonce ještě rozšířit. Je možné (alespoň teoreticky) obnovit upload po nečekaném ukončení nebo pádu prohlížeče. Zde ale nastává problém, že po uzavření prohlížeče jsou data odesílaného souboru z paměti smazána. Uživatel tak musí znova vybrat požadovaný soubor před tím, než jej můžeme rozdělit na části a začít znova odesílat.

Také by se dalo použít nové IndexedDB API, pomocí kterého si před vlastním zahájením uploadu soubor uložíme. Po pádu prohlížeče pak stačí neodeslaný soubor načíst z databáze a začít ihned odesílat.

Komentáře

Odebírat
Upozornit na
guest
5 Komentářů
Nejstarší
Nejnovější Nejvíce hlasů
karlos

Aneb proc veci resit jednoduse… Stacilo se 10 let zpatky mrknout do Opery…

kkk

Můžete, prosím, vaši myšlenku rozvést?
(Operu používám nějakých 6 let a opravdu nevím, co myslíte.)

Jan Pobořil

Jak ale řešit upload souborů velkých v řádech GB, které se určitě nevejdou do RAM ani databáze v prohlížeči?

Martin Malý

Přimlouval bych se za „jinými prostředky, než je HTTP a formulář na webu“.

georgiksk

Pekný článok. Viac takýchto.

Len podotknem, že funckia slice bola premenovaná aj vo webkit-based prehliadačoch na webkitSlice. Zároveň sa zmenil aj význam parametrov.

Pár poznámok je tu: http://georgik.sinusgear.com/2011/05/06/html5-api-file-slice-changed/

Proč vám model napíše Python, i když jste si řekli o Rust

AI
Komentáře: 0
Výkonnostní propast mezi Pythonem a ostatními jazyky se za poslední rok a půl skoro zavřela. Dnešní modely zvládají Rust i Go srovnatelně dobře. Když jim ale volbu necháte, sáhnou stejně po Pythonu. A u knihoven je to ještě výraznější: mezi funkčně srovnatelnými balíčky je až 84procentní rozdíl v kvalitě generovaného kódu. Proč to tak je a co s tím.

WP2Shell: Kritická hrozba pro samotné jádro WordPressu. Útočníci mohou získat kontrolu nad webem

Zranitelnost ve WordPressu není žádná novinka. Kdo provozuje weby postavené na této platformě, ví, že bezpečnostní záplaty chodí prakticky pořád. O to větší pozornost by měla vzbudit chyba, u které nic z toho neplatí. A přesně takový je případ zranitelnosti, která dostala přezdívku wp2shell. potřeb. Zranitelný kontaktní formulář, děravý e-shopový plugin, opomenutá kontrola oprávnění v nějaké obskurní rozšiřující knihovně – to je denní chleba každého, kdo sleduje bezpečnostní feedy. Zpráva „nová chyba ve WordPress pluginu“ má tak nízkou informační hodnotu, že ji většina lidí přejde bez mrknutí oka.

Mýtus jedné aplikace: proč PWA nenahradí vývoj pro každou platformu

PWA mohou webu přidat ikonu na ploše, fungování bez připojení, notifikace a některé systémové funkce. Nejsou ale cestou k jednomu klientu pro všechny platformy. Vyplatí se tam, kde se lidé k webu vracejí a ocení okamžitý vstup z odkazu. Jakmile aplikace musí spolehlivě běžet na pozadí nebo fungovat stejně na každém zařízení, bývá vhodnější nativní řešení.