Introduction
In the previous chapter we got some intuition behind the File
interface and how it's used to provide us with information about a selected file.
In this chapter we shall consider the parent class of this interface i.e Blob
and see how to use blob objects in our applications, as a superset of File
to operate raw file-like data on the go.
What is a blob?
Essentially:
Unlike a buffer, a blob is a broader and more capable interface not just used to store binary data (as is a buffer), but also used in various other interfaces such as FileReader()
.
The name 'blob' stands for Binary Large Object and has its origins in the database languages' world.
Appreciate the fact that the name 'blob' well describes the underlying concept i.e a blob is a large object in binary format.
But what's the point of having a blob?
The reason behind the idea of blobs in JavaScript was to allow developers a way to easily work with raw data and use it in place of actual files.
For example, processing an image file sent using AJAX, or generating a file-like object dynamically.
The File
interface was sensibly taken to represent only direct files on the local filesystem - we select a file, the browser generates a File
object out of it and finally we use this object for various things such as reading it using FileReader()
and so on.
The Blob
interface was taken to be a general case i.e it could be used to represent file-like objects as well.
Being able to use file-functionality in the language shouldn't just be limited to real files - we should also be able to use the same functionality on dynamically created file-like objects.
So what we shall understand from the discussion above is that:
Following from this, the File
interface inherits from Blob
, and is used to represent actual files (selected using the ways discussed in File APIs - File Object).
For the latter, i.e representing a file-like object, we've got the Blob
interface itself.
Now that we've got some solid explanation on the concept of blobs in JavaScript, we can proceed onwards to see how to create one?
Syntax of creating a blob
Creating a blob is very simple - just use the Blob()
constructor.
Shown below is the general form of the constructor:
new Blob(blobParts[, options]);
The first blobParts
argument is an array of string, ArrayBuffer
or Blob
objects. It's the actual data of the blob. Omitting it will create an empty blob.
The second options
argument is an object configuring the blob, with two further properties:
type
- a string to indicate the MIME type of the data. This property basically hints the blob of the underlying data - if it's specified the blob can easily parse the data, otherwise it may produce unexpected results.endings
to indicate how line endings shall be treated withinblobParts
. Note that this property isn't frequently used.
Since the property type
requires the developer to know a bit about MIME types, following we've compiled a list of the most common of them.
MIME type | Description |
---|---|
text/plain | Plain text document |
text/html | HTML document |
text/javascript | JavaScript file |
text/css | CSS file |
application/json | JSON file |
application/pdf | PDF file |
application/xml | XML file |
image/jpeg | JPEG image |
image/png | PNG image |
image/gif | GIF image |
image/svg+xml | SVG image |
audio/mpeg | MP3 file |
video/mpeg | MP4 file |
type
as a string. For example text/plain
will be written as "text/plain"
.With the syntax well set, let's now incorporate all this with the essence of a real example.
Creating a blob
We'll create a simple, plain text file with the content "Hello World"
, using Blob()
.
var blob = new Blob(["Hello World"], {type: "text/plain"});
This can be termed as 'dynamic file creation' - simply because a file-like object is being created on-the-go.
Anyways, let's dissect the code above:
First the Blob()
constructor is called, as usual with the new
keyword.
Then, as the first argument, we pass an array containing just one string - "Hello World"
.
Regardless of whatever data you want to put in a blob, it has to be wrapped up in an array. This is because the internal engine iterates through the array and lines up the raw data of each item next to one another.
Afterwards, as the second argument, we pass an object with type
set to "text/plain"
to hint the blob that the underlying data is for a plain text file.
With this done we can now interact with the blob
object and retrieve its properties.
A Blob
object essentially has two properties:
size
- the size of the underlying data, in bytes.type
- the MIME type of the blob.
For our blob
object above, can you determine the values of both these properties?
"Hello World"
is UTF-8 encoded and hence each of its characters take up 1 byte!Let's see it in real:
var blob = new Blob(["Hello World"], {type: "text/plain"});
console.log(blob.size); // 11
console.log(blob.type); // "text/plain"
As you can see, blob.size
returns 11
, which is synonymous with the length of the string "Hello World"
.
Furthermore, blob.type
returns the same type
value we sent within the options
object, when creating the blob i.e "text/plain"
.
In fact, type
returns whatever value you assigned to the same property in the options
parameter of the Blob()
constructor.
Now just constructing a blob isn't very useful - we need to utilise it somewhere, of which the most sound option is to create an object URL out of it. Following we do just this very thing.
<iframe></iframe>
var iframe = document.getElementsByTagName("iframe")[0];
var blob = new Blob(["Hello World"], {type: "text/plain"});
// create an object URL for the blob constructed above
iframe.src = URL.createObjectURL(blob);
Slicing a blob
Besides employing the Blob()
constructor to create an oven-fresh blob, one can also create a blob out of a blob. We refer to this as slicing a blob.
Slicing blobs is much like slicing arrays - we provide a starting point and an ending point, and consequently the object is sliced around both those points.
The question is: how do we slice a blob?
Well, once again, it's just like how we slice arrays i.e using the slice()
method.
The slice()
method, inherited by blobs from Blob.prototype
, takes three optional arguments and returns a new blob meeting the criteria set by those arguments.
blob.slice([start,[ end[, contentType]]]);
The arguments work as follows:
- The first parameter
start
, sets the starting point of the slice i.e it tells where the slicing should begin. The default value is0
, which means that the slicing should begin right from the first byte. - The second parameter
end
, sets the ending point of the slice. It is exclusive, which means that slicing won't be done upto that byte, but rather just before it. The default value is thesize
of the blob. - The third and last
contentType
parameter sets the MIME type of the new blob. If omitted, it defaults to the originaltype
of the blob.
slice()
can also be given negative values. If this happens, then the offsets begin from the end of the blob, instead of from the start. We'll see an example below.Let's consider an example.
Following, we slice a blob from the "Hello World"
blob, we created above, create an object URL for it and finally assign this to the iframe's src
:
var iframe = document.getElementsByTagName("iframe")[0];
var blob = new Blob(["Hello World"], {type: "text/plain"});
// create a new blob by slicing the blob above
// from bytes 0 to 9
var subBlob = blob.slice(0, 10);
// create an object URL for the blob constructed above
iframe.src = URL.createObjectURL(subBlob);
As expected, the content of subBlob
reduces down to "Hello Worl"
since we've omitted the last byte in slicing blob
.
Let's consider a more practical example, showing the purpose of the contentType
argument.
Below we create a blob of type "text/plain"
, which has some CSS code in it along with some other content.
var blob = new Blob(["styles.css - body{background-color: yellow}"], {type: "text/plain"})
Now obviously because invalid stylesheets aren't parsed by browsers, creating an object url for this blob and then assigning it to a link
element will have no effect.
We first need to clear away the 'styles.css - '
substring from this file. And that we'll do using the slice()
method.
var blob = new Blob(["styles.css - body{background-color: yellow}"], {type: "text/plain"});
var slicedBlob = blob.slice(13, blob.size, "text/css");
The first argument 13
tells to start slicing at the thirteenth byte i.e the first 'b'
character. The second argument tells to slice till the end. The last argument, which specifies the MIME type of the sliced blob, is given here as a safe measure.
The thing is that blob
has type "text/plain"
, so omitting this argument while performing the slice might have the consequence that some browsers don't parse slicedBlob
.
Likewise we provide the third argument "text/css"
, to ensure that all browsers recognise sliceBlob
as a CSS file.
Below we use slicedBlob
to generate a stylesheet that will be used to style the HTML document:
var blob = new Blob(["styles.css - body{background-color: yellow}"], {type: "text/plain"});
// extract "body{background-color: yellow}" from blob
var slicedBlob = blob.slice(13, blob.size, "text/css");
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = URL.createObjectURL(slicedBlob);
document.head.appendChild(link);