JavaScript Blobs

Chapter 19 16 mins

Learning outcomes:

  1. What are blobs
  2. The Blob interface
  3. Creating blobs
  4. Slicing blobs

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:

A blob is an object in JavaScript that represents raw file-like data.

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.

A blob doesn't always have to be a large binary object - sometimes it can be small too! However, usually, it isn't.

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.

We're using the term 'file-like object' here, to emphasize on the fact that we're not referring to actual files on the local filesystem, but rather to an object that resembles the attributes of an actual file.

So what we shall understand from the discussion above is that:

A blob is used to represent any file-like data; the data can be obtained from an actual file OR it can be obtained from a file-like object.

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:

  1. 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.
  2. endings to indicate how line endings shall be treated within blobParts. 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 typeDescription
text/plainPlain text document
text/htmlHTML document
text/javascriptJavaScript file
text/cssCSS file
application/jsonJSON file
application/pdfPDF file
application/xmlXML file
image/jpegJPEG image
image/pngPNG image
image/gifGIF image
image/svg+xmlSVG image
audio/mpegMP3 file
video/mpegMP4 file
The MIME types shown here will go in the property 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:

  1. size - the size of the underlying data, in bytes.
  2. type - the MIME type of the blob.

For our blob object above, can you determine the values of both these properties?

Remember that the string "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.

For a detailed guide to object URLs please read JavaScript Object URLs.
<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);

Live Example

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:

  1. The first parameter start, sets the starting point of the slice i.e it tells where the slicing should begin. The default value is 0, which means that the slicing should begin right from the first byte.
  2. 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 the size of the blob.
  3. The third and last contentType parameter sets the MIME type of the new blob. If omitted, it defaults to the original type of the blob.
The first two parameters of 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);

Live Example

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);

Live Example

"I created Codeguage to save you from falling into the same learning conundrums that I fell into."

— Bilal Adnan, Founder of Codeguage