up
This commit is contained in:
parent
31f83a5e1b
commit
4ffd79e337
65 changed files with 1481 additions and 28000 deletions
|
@ -4,7 +4,7 @@
|
|||
|
||||
In the browser, there are additional higher-level objects, described in [File API](https://www.w3.org/TR/FileAPI/).
|
||||
|
||||
`Blob` consists of an optional string `type` (a MIME-type usually), plus `blobParts` this a sequence of other `Blob` objects, strings and `BufferSources`.
|
||||
`Blob` consists of an optional string `type` (a MIME-type usually), plus `blobParts` -- a sequence of other `Blob` objects, strings and `BufferSources`.
|
||||
|
||||

|
||||
|
||||
|
@ -16,12 +16,28 @@ The constructor syntax is:
|
|||
new Blob(blobParts, options);
|
||||
```
|
||||
|
||||
- **`blobParts`** is an array of Blob/BufferSource/String values.
|
||||
- **`blobParts`** is an array of `Blob`/`BufferSource`/`String` values.
|
||||
- **`options`** optional object:
|
||||
- **`type`** -- blob type, usually MIME-type, e.g. `image/png`,
|
||||
- **`endings`** -- whether to transform end-of-line `\r\n` to `\n` end vise versa, to make the blob correspond to current OS newlines. By default `"transparent"` (do nothing), but also can be `"native"` (transform).
|
||||
- **`endings`** -- whether to transform end-of-line to make the blob correspond to current OS newlines (`\r\n` or `\n`). By default `"transparent"` (do nothing), but also can be `"native"` (transform).
|
||||
|
||||
Then we can access blob as a whole, or extract slices with:
|
||||
For example:
|
||||
|
||||
```js
|
||||
// create Blob from a string
|
||||
let blob = new Blob(["<html>…</html>"], {type: 'text/html'});
|
||||
// please note: the first argument must be an array [...]
|
||||
```
|
||||
|
||||
```js
|
||||
// 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'});
|
||||
```
|
||||
|
||||
|
||||
We can extract blob slices with:
|
||||
|
||||
```js
|
||||
blob.slice([byteStart], [byteEnd], [contentType]);
|
||||
|
@ -33,11 +49,15 @@ blob.slice([byteStart], [byteEnd], [contentType]);
|
|||
|
||||
The arguments are similar to `array.slice`, negative numbers are allowed too.
|
||||
|
||||
We can't change data directly in a blob, but we can copy parts of blobs, create new blobs from them, mix them and so on. This behavior is similar to Javascript strings: we can't change a character in a string, but we can make a new corrected string.
|
||||
```smart header="Blobs are immutable"
|
||||
We can't change data directly in a blob, but we can slice parts of blobs, create new blobs from them, mix them into a new blob and so on.
|
||||
|
||||
This behavior is similar to Javascript strings: we can't change a character in a string, but we can make a new corrected string.
|
||||
```
|
||||
|
||||
## Blob as URL
|
||||
|
||||
A Blob can be easily used in an URL for `<a>`, `<img>` or other tags.
|
||||
A Blob can be easily used as an URL for `<a>`, `<img>` or other tags, to show its contents.
|
||||
|
||||
Let's start with a simple example. By clicking on a link you download a dynamically-generated blob with `hello world` contents as a file:
|
||||
|
||||
|
@ -46,18 +66,15 @@ Let's start with a simple example. By clicking on a link you download a dynamica
|
|||
<a download="hello.txt" href='#' id="link">Download</a>
|
||||
|
||||
<script>
|
||||
let binary = new Uint8Array([72, 101, 108, 108, 111]); // "hello" in binary form
|
||||
|
||||
// create blob of multiple parts
|
||||
let blob = new Blob([binary, ' ', 'world'], {type: 'text/plain'});
|
||||
let blob = new Blob(["Hello, world!"], {type: 'text/plain'});
|
||||
|
||||
link.href = URL.createObjectURL(blob);
|
||||
</script>
|
||||
```
|
||||
|
||||
We can also create a link in Javascript and simulate a click by `link.click()`, then download starts authomatically.
|
||||
We can also create a link dynamically in Javascript and simulate a click by `link.click()`, then download starts authomatically.
|
||||
|
||||
Here's the similar "on the fly" blob creation and download code, now without HTML:
|
||||
Here's the similar "on the fly" blob creation and download code, but without HTML:
|
||||
|
||||
```js run
|
||||
let link = document.createElement('a');
|
||||
|
@ -80,19 +97,19 @@ That's what the generated url looks like:
|
|||
blob:https://javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273
|
||||
```
|
||||
|
||||
The browser keeps an internal url -> blob mapping. For each url generated by `URL.createObjectURL`, it stores a reference to the corresponding blob.
|
||||
The browser for each url generated by `URL.createObjectURL` stores an the url -> blob mapping internally. So such urls are short, but allow to access the blob.
|
||||
|
||||
A generated url is only valid while the current document is open. And it allows to reference the blob in `<img>`, `<a>`, any other object that expects an URL.
|
||||
A generated url is only valid while the current document is open. And it allows to reference the blob in `<img>`, `<a>`, any other object that expects an url.
|
||||
|
||||
There's a side-effect though. While there's an mapping for a blob, the blob itself resides in the memory. The browser can't free it.
|
||||
|
||||
The mapping is automatically cleared on document unload, so blobs are freed then. But if an app is long-living, then that doesn't happen. So if we create an URL, that blob will hang in memory, even if not needed any more.
|
||||
The mapping is automatically cleared on document unload, so blobs are freed then. But if an app is long-living, then that doesn't happen soon. So if we create an URL, that blob will hang in memory, even if not needed any more.
|
||||
|
||||
**`URL.revokeObjectURL(url)` removes the reference from the internal mapping, thus allowing the blob to be deleted (if there are no other references), and the memory to be freed.**
|
||||
|
||||
In the last example, we intend the blob to be used only once, for instant downloading, so we call `URL.revokeObjectURL(link.href)` immediately.
|
||||
|
||||
In the previous example though, with the clickable HTML-link, we dont't call `URL.revokeObjectURL(link.href)`, because that would make the blob url invalid. After the revocation, as the mapping is removed, the url doesn't work any more.
|
||||
In the previous example though, with the clickable HTML-link, we don't call `URL.revokeObjectURL(link.href)`, because that would make the blob url invalid. After the revocation, as the mapping is removed, the url doesn't work any more.
|
||||
|
||||
## Blob to base64
|
||||
|
||||
|
@ -111,7 +128,7 @@ For instance, here's a smiley:
|
|||
The browser will decode the string and show the image: <img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7">
|
||||
|
||||
|
||||
To transform a blob into base64 and vise-versa, we'll use built-in `FileReader/FileWriter` objects. In the [next chapter](info:file) we'll cover those more in-detail. For now, it's important only that they can read a blob as data url, that we can assign it to `link.href`.
|
||||
To transform a blob into base64, we'll use the built-in `FileReader` object. It can read data from Blobs in multiple formats. In the [next chapter](info:file) we'll cover it more in-depth.
|
||||
|
||||
Here's the demo of downloading a blob, now via base-64:
|
||||
|
||||
|
@ -123,16 +140,13 @@ 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();
|
||||
};
|
||||
|
||||
*!*
|
||||
reader.readAsDataURL(blob); // converts the blob to base64 and calls onload
|
||||
*/!*
|
||||
```
|
||||
|
||||
Both ways of making an URL of a blob are usable. But usually `URL.createObjectURL(blob)` is simpler and faster.
|
||||
|
@ -144,57 +158,70 @@ Both ways of making an URL of a blob are usable. But usually `URL.createObjectUR
|
|||
- Performance and memory losses on big blobs for encoding.
|
||||
```
|
||||
|
||||
## Image or a page to blob
|
||||
## Image to blob
|
||||
|
||||
We can create a blob of an image, or even a page screenshot.
|
||||
We can create a blob of an image, an image part, or even make a page screenshot. That's handy to upload it somewhere.
|
||||
|
||||
That's usually done via `<canvas>` element:
|
||||
Image operations are done via `<canvas>` element:
|
||||
|
||||
1. We draw an existing image on canvas using [canvas.drawImage](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage).
|
||||
2. Canvas [.toBlob(callback, format, quality)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) creates a blob and calls `callback` with it.
|
||||
1. Draw an image (or its part) on canvas using [canvas.drawImage](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage).
|
||||
2. Call canvas method [.toBlob(callback, format, quality)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) that creates a blob and runs `callback` with it when done.
|
||||
|
||||
In the example below, an image is just copied, but we could cut from it, or transform it on canvas prior to making a blob:
|
||||
|
||||
```js run
|
||||
let img = document.querySelector('img'); // just any image
|
||||
// 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');
|
||||
|
||||
context.drawImage(img, 0, 0); // allows to cut image
|
||||
// we can context.rotate() as well, and do many other things
|
||||
// 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 opereation, callback is called when done
|
||||
canvas.toBlob(function(blob) {
|
||||
// download the 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');
|
||||
```
|
||||
|
||||
For screenshotting a page, we can use a library such as <https://github.com/niklasvh/html2canvas> to draw the screenshot on `<canvas>`, and then download it the same way.
|
||||
If we prefer `async/await` instead of callbacks:
|
||||
```js
|
||||
let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));
|
||||
```
|
||||
|
||||
For screenshotting a page, we can use a library such as <https://github.com/niklasvh/html2canvas>. What it does is just walks the page and draws it on `<canvas>`. Then we can get a blob of it the same way as above.
|
||||
|
||||
## From Blob to ArrayBuffer
|
||||
|
||||
The `Blob` constructor allows to create a blob from almost anything.
|
||||
The `Blob` constructor allows to create a blob from almost anything, including any `BufferSource`.
|
||||
|
||||
But we can also revert back to lowest-level `ArrayBuffer` using `FileReader`:
|
||||
But if we need to perform low-level processing, we can get the lowest-level `ArrayBuffer` from it using `FileReader`:
|
||||
|
||||
```js
|
||||
// get arrayBuffer from blob
|
||||
let fileReader = new FileReader();
|
||||
fileReader.onload = function(event) {
|
||||
let arrayBuffer = fileReader.result;
|
||||
};
|
||||
|
||||
*!*
|
||||
fileReader.readAsArrayBuffer(blob);
|
||||
*/!*
|
||||
|
||||
fileReader.onload = function(event) {
|
||||
let arrayBuffer = fileReader.result;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -204,9 +231,9 @@ While `ArrayBuffer`, `Uint8Array` and other `BufferSource` are "binary data", a
|
|||
|
||||
That makes Blobs convenient for upload/download operations, that are so common in the browser.
|
||||
|
||||
Methods that perform web-requests, such as [XMLHttpRequest](info:xmlhttprequest), [fetch](info:fetch) and so on, can work with `Blob` natively, as well as with other binary types.
|
||||
Methods that perform web-requests, such as [XMLHttpRequest](info:xmlhttprequest), [fetch](info:fetch-basics) and so on, can work with `Blob` natively, as well as with other binary types.
|
||||
|
||||
That said, it's still possible to convert betweeen `Blob` and low-level binary data types:
|
||||
We can easily convert betweeen `Blob` and low-level binary data types:
|
||||
|
||||
- We can make a Blob from a typed array using `new Blob(...)` constructor.
|
||||
- We can get back `ArrayBuffer` from a Blob using `FileReader`, and then create a view over it for low-level binary processing.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue