Webspire

Administrační systém

Servírování obsahu webovou aplikací po částech

Některé webové prohlížeče si mohou vyžádat odpověď od serveru po částech. Toto se typicky děje pro mediální obsah jako jsou datově náročná videa a obrázky.

Aktuální webové servery již takové požadavky zvládají a pokud požadavek na ně obsahuje patřičnou hlavičku Range, pak korektně zasílají odpověď po částech:

Hlavičky požadavku na server

GET /z4d4kWk.jpg HTTP/1.1
Host: i.imgur.com
Range: bytes=0-1023

Hlavičky odpovědi serveru

HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/146515
Content-Length: 1024

Pokud si chcete ověřit, zda váš webový server podporuje odpovědi po částech, stačí se jej dotázat přes curl na nějaký soubor, např. curl -I URL, a pokud se v hlavičkách odpovědi nachází hlavička Accept-Ranges: bytes, máte vyhráno.

Pokud však odpověď negeneruje webový server, ale např. aplikace, pak se o tuto funkcionalitu musíte postarat sami. V opačném případě se vám může stát, že vám prohlížeč (pozorováno v Chrome a Safari) nepřehraje HTML 5 video nebo nezobrazí kvalitní fotku na pozadí.

Řešení je poměrně triviální. Při generování odpovědi ze serveru je potřeba kontrolovat, zda požadavek na server neobsahuje již zmíněnou hlavičku Range. V PHP je možné ji najít v globální proměnné $_SERVER pod klíčem HTTP_RANGE a její hodnotou je část souboru v bytech, kterou prohlížeč požaduje v odpovědi. Následuje ukázka kódu v PHP pro sestavení odpovědi aplikace.

Čtení souboru po částech

// ...

$handle = fopen($path, 'rb'); $size = filesize($path);

// $range contains value from $_SERVER['HTTP_RANGE']
list($from, $to) = getContentRange($range, $size);

fseek($handle, $from);

while (ftell($handle) < $to) {
    echo fread($handle, 1024);
}

fclose($handle);

// ...

/**
* @param string $range Request file range in bytes
* @param int $size Total file size in bytes
*
* @return int[]
*/
function getContentRange(string $range, int $size): array
{
    // parse the range header to get the byte offset
    $ranges = array_map(
        'intval', // parse the parts into integer
        explode(
            '-', // the range separator
            substr($range, 6) // skip the `bytes=` part of the header
        )
    );

    // if the last range param is empty, it means the EOF (End of File)
    if (empty($ranges[1])) {
        $ranges[1] = $size - 1;
    }

    return $ranges;
}

Dále je potřeba v rámci odpovědi zaslat i správné hlavičky.

Nastavení správných hlaviček

// ...
header('HTTP/1.1 206 Partial Content');
header('Accept-Ranges: bytes');
header("Content-Range: bytes $from-$to/$size");
header("Content-Length: $to-$from+1");
// ...

Pokud v rámci aplikace tyto kroky dodržíte, pak nejen že docílíte správné funkčnosti, ale také lepší odezvy aplikace a UX pro návštěvníka. Prohlížeč je nyní schopen začít přehrávat video, aniž by jej měl kompletně stažené. Zároveň nejsou blokovány další požadavky na ostatní součásti webu, neboť se nečeká na stažení celého videa.

Reference

Sdílet:
###message