Kapka

ArrayBuffer a pohledy jsou součástí standardu ECMA, součástí JavaScriptu.

V prohlížeči jsou další objekty vyšší úrovně popsané v File API, konkrétně Blob .

Blob sestává z volitelného řetězce type (obvykle typ MIME) plus blobParts – sekvence dalších Blob objekty, řetězce a BufferSource .

Syntaxe konstruktoru je:

new Blob(blobParts, options);
  • blobParts je pole Blob /BufferSource /String hodnoty.
  • options volitelný objekt:
    • type Blob typu, obvykle typu MIME, např. image/png ,
    • endings – zda ​​transformovat konec řádku na Blob odpovídají aktuálním novým řádkům OS (\r\n nebo \n ). Ve výchozím nastavení "transparent" (nedělat nic), ale také může být "native" (transformace).

Například:

// create Blob from a string
let blob = new Blob(["<html>…</html>"], {type: 'text/html'});
// please note: the first argument must be an array [...]
// create Blob from a typed array and strings
let hello = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" in binary form

let blob = new Blob([hello, ' ', 'world'], {type: 'text/plain'});

Můžeme extrahovat Blob plátky s:

blob.slice([byteStart], [byteEnd], [contentType]);
  • byteStart – počáteční bajt, ve výchozím nastavení 0.
  • byteEnd – poslední bajt (výhradně, ve výchozím nastavení až do konce).
  • contentType type nového blob, ve výchozím nastavení stejný jako zdroj.

Argumenty jsou podobné array.slice , jsou povolena i záporná čísla.

Blob objekty jsou neměnné

Nemůžeme měnit data přímo v Blob , ale můžeme rozdělit části Blob , vytvořte nový Blob objekty z nich, smíchejte je do nového Blob a tak dále.

Toto chování je podobné řetězcům JavaScriptu:nemůžeme změnit znak v řetězci, ale můžeme vytvořit nový opravený řetězec.

Blob jako adresa URL

Blob lze snadno použít jako adresu URL pro <a> , <img> nebo jiné značky, aby se zobrazil její obsah.

Díky type , můžeme také stáhnout/nahrát Blob objektů a type přirozeně se změní na Content-Type v síťových požadavcích.

Začněme jednoduchým příkladem. Kliknutím na odkaz stáhnete dynamicky generovaný Blob s hello world obsah jako soubor:

<!-- download attribute forces the browser to download instead of navigating -->
<a download="hello.txt" href='#' id="link">Download</a>

<script>
let blob = new Blob(["Hello, world!"], {type: 'text/plain'});

link.href = URL.createObjectURL(blob);
</script>

Můžeme také vytvořit odkaz dynamicky v JavaScriptu a simulovat kliknutí pomocí link.click() a stahování se spustí automaticky.

Zde je podobný kód, který způsobí, že si uživatel stáhne dynamicky vytvořený Blob , bez jakéhokoli HTML:

let link = document.createElement('a');
link.download = 'hello.txt';

let blob = new Blob(['Hello, world!'], {type: 'text/plain'});

link.href = URL.createObjectURL(blob);

link.click();

URL.revokeObjectURL(link.href);

URL.createObjectURL trvá Blob a vytvoří pro něj jedinečnou adresu URL ve tvaru blob:<origin>/<uuid> .

To je hodnota link.href vypadá takto:

blob:https://javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273

Pro každou adresu URL vygenerovanou URL.createObjectURL prohlížeč ukládá URL → Blob mapování interně. Takže takové adresy URL jsou krátké, ale umožňují přístup k Blob .

Vygenerovaná adresa URL (a tedy i odkaz na ni) je platná pouze v aktuálním dokumentu, dokud je otevřený. A umožňuje odkazovat na Blob v <img> , <a> , v podstatě jakýkoli jiný objekt, který očekává URL.

Je tu však vedlejší účinek. Zatímco existuje mapování pro Blob , Blob sám sídlí v paměti. Prohlížeč jej nemůže uvolnit.

Mapování se při uvolnění dokumentu automaticky vymaže, takže Blob objekty jsou pak osvobozeny. Ale pokud je aplikace dlouhodobá, pak se to nestane brzy.

Pokud tedy vytvoříme adresu URL, bude to Blob zůstane v paměti, i když už nebude potřeba.

URL.revokeObjectURL(url) odstraní odkaz z interního mapování, čímž povolí Blob k vymazání (pokud neexistují žádné další odkazy) a paměť, která má být uvolněna.

V posledním příkladu máme na mysli Blob použít pouze jednou, pro okamžité stažení, proto nazýváme URL.revokeObjectURL(link.href) okamžitě.

V předchozím příkladu s odkazem HTML, na který lze kliknout, nevoláme URL.revokeObjectURL(link.href) , protože by to znamenalo Blob neplatná adresa URL. Po zrušení, když je mapování odstraněno, adresa URL již nefunguje.

Bloba na base64

Alternativa k URL.createObjectURL je převést Blob do řetězce zakódovaného v base64.

Toto kódování představuje binární data jako řetězec ultra bezpečných „čitelných“ znaků s kódy ASCII od 0 do 64. A co je důležitější – toto kódování můžeme použít v „data-urls“.

Datová adresa URL má tvar data:[<mediatype>][;base64],<data> . Takové adresy URL můžeme použít všude, stejně jako „běžné“ adresy URL.

Zde je například smajlík:

<img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7">

Prohlížeč dekóduje řetězec a zobrazí obrázek:

Chcete-li transformovat Blob do base64, použijeme vestavěný FileReader objekt. Dokáže číst data z objektů Blob v různých formátech. V další kapitole se tomu budeme věnovat podrobněji.

Zde je ukázka stahování blobu, nyní přes base-64:

let link = document.createElement('a');
link.download = 'hello.txt';

let blob = new Blob(['Hello, world!'], {type: 'text/plain'});

let reader = new FileReader();
reader.readAsDataURL(blob); // converts the blob to base64 and calls onload

reader.onload = function() {
  link.href = reader.result; // data url
  link.click();
};

Oba způsoby vytvoření adresy URL z Blob jsou použitelné. Ale obvykle URL.createObjectURL(blob) je jednodušší a rychlejší.

URL.createObjectURL(blob)
  • Pokud nám záleží na paměti, musíme je odvolat.
  • Přímý přístup k blob, žádné „kódování/dekódování“
Blob na adresu URL dat
  • Není třeba nic odvolávat.
  • Ztráty výkonu a paměti na velkých Blob objekty pro kódování.

Obrázek do blob

Můžeme vytvořit Blob obrázku, části obrázku nebo dokonce vytvořit snímek stránky. Je užitečné to někam nahrát.

Operace s obrázky se provádějí pomocí <canvas> prvek:

  1. Nakreslete obrázek (nebo jeho část) na plátno pomocí canvas.drawImage.
  2. Call canvas method .toBlob(callback, format, quality), která vytvoří Blob a spustí callback s tím, až bude hotovo.

V níže uvedeném příkladu je obrázek pouze zkopírován, ale před vytvořením blob z něj můžeme vyjmout nebo transformovat na plátno:

// take any image
let img = document.querySelector('img');

// make <canvas> of the same size
let canvas = document.createElement('canvas');
canvas.width = img.clientWidth;
canvas.height = img.clientHeight;

let context = canvas.getContext('2d');

// copy image to it (this method allows to cut image)
context.drawImage(img, 0, 0);
// we can context.rotate(), and do many other things on canvas

// toBlob is async operation, callback is called when done
canvas.toBlob(function(blob) {
  // blob ready, download it
  let link = document.createElement('a');
  link.download = 'example.png';

  link.href = URL.createObjectURL(blob);
  link.click();

  // delete the internal blob reference, to let the browser clear memory from it
  URL.revokeObjectURL(link.href);
}, 'image/png');

Pokud dáváme přednost async/await místo zpětných volání:

let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));

Pro screenshot stránky můžeme použít knihovnu, jako je https://github.com/niklasvh/html2canvas. To, co dělá, je, že prochází stránku a kreslí ji na <canvas> . Pak můžeme získat Blob stejným způsobem jako výše.

Z blobu do ArrayBuffer

Blob konstruktor umožňuje vytvořit blob z téměř čehokoli, včetně libovolného BufferSource .

Pokud ale potřebujeme provést nízkoúrovňové zpracování, můžeme získat nejnižší úroveň ArrayBuffer od blob.arrayBuffer() :

// get arrayBuffer from blob
const bufferPromise = await blob.arrayBuffer();

// or
blob.arrayBuffer().then(buffer => /* process the ArrayBuffer */);

Z blobu do streamu

Když čteme a zapisujeme do blobu většího než 2 GB , použití arrayBuffer se pro nás stává náročnější na paměť. V tomto okamžiku můžeme objekt blob přímo převést na stream.

Proud je speciální objekt, který z něj umožňuje číst (nebo do něj zapisovat) po částech. Zde je to mimo náš rozsah, ale zde je příklad a více si můžete přečíst na https://developer.mozilla.org/en-US/docs/Web/API/Streams_API. Proudy jsou vhodné pro data, která jsou vhodná pro zpracování po jednotlivých kusech.

Blob rozhraní stream() metoda vrací ReadableStream který po přečtení vrátí data obsažená v Blob .

Pak z něj můžeme číst takto:

// get readableStream from blob
const readableStream = blob.stream();
const stream = readableStream.getReader();

while (true) {
  // for each iteration: value is the next blob fragment
  let { done, value } = await stream.read();
  if (done) {
    // no more data in the stream
    console.log('all blob processed.');
    break;
  }

   // do something with the data portion we've just read from the blob
  console.log(value);
}

Shrnutí

Zatímco ArrayBuffer , Uint8Array a další BufferSource jsou „binární data“, objekt Blob představuje „binární data s typem“.

Díky tomu jsou objekty Blob vhodné pro operace nahrávání/stahování, které jsou v prohlížeči tak běžné.

Metody, které provádějí webové požadavky, jako je XMLHttpRequest, načítání a tak dále, mohou pracovat s Blob nativně, stejně jako s jinými binárními typy.

Můžeme snadno převádět mezi Blob a nízkoúrovňové binární datové typy:

  • Můžeme vytvořit Blob z typovaného pole pomocí new Blob(...) konstruktor.
  • Můžeme získat zpět ArrayBuffer z objektu Blob pomocí blob.arrayBuffer() a poté nad ním vytvořte pohled pro nízkoúrovňové binární zpracování.

Konverzní streamy jsou velmi užitečné, když potřebujeme zpracovat velké blob. Můžete snadno vytvořit ReadableStream z kapky. Blob rozhraní stream() metoda vrací ReadableStream který po přečtení vrátí data obsažená v blobu.