Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Charlie Arehart
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Charlie Arehart@carehart )

Downloading Text Using Blobs, URL.createObjectURL(), And The Anchor Download Attribute In JavaScript

By Ben Nadel on

About a month ago, I discovered that you could use Plain Text in a Data URI when programmatically prompting a user to download content. Prior to that, I had assumed that all Data URIs had to be Base64-encoded. In response to that post, several of my co-workers (Adam DiCarlo and Dave Johnson) warned me that text-based URIs were constrained by size limitations. And, that I should look into using a Blob URI instead. Now, I have used Blobs in the past to render image previews (thank you Jonathan Rowny); but, I've never created a Blob myself. As such, I wanted to take my previous text-download demo and revamp it to use Blobs and the URL.createObjectURL() method.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

View this code in my JavaScript Demos project on GitHub.

In the previous demo, I was setting an anchor tag's "href" attribute to point to a Data URI in the form of:

href="data:text/plain;charset=utf-8,........."

In this demo, we're going to be using a similar approach. Except, instead of a Data URI, we're going to point the "href" attribute to an in-memory object reference. This reference will be generated by the URL.createObjectURL() method. This method takes a Blob (or Blob-like object) and returns a String that can be used in any place that a URL might be used (such as an HREF attribute).

When you create a Blob URI, the browser holds onto the referenced memory until the document is unloaded; or, until you explicitly revoke the URL. Depending on what your application is doing, this may or may not be a concern. But, if you're generating a lot of these URLs, you can control the memory consumption more granularly by removing old Blob URIs with the URL.revokeObjectURL(blogURI) method.

Of course, in order to use the URL.createObjectURL() method, we need to have a Blob. Luckily, creating a Blob from a plain-text value is as simple as calling the Blob constructor and passing in the text value:

  • var blob = new Blob(
  • [ plainTextValue ],
  • {
  • type : "text/plain;charset=utf-8"
  • }
  • );

This returns a Blob instance, which we can then pass to URL.createObjectURL(). To see this in action, I've revamped my previous demo to convert the contents of a Textarea element into a Blob URI which is then made downloadable through the use of the "download" attribute on an anchor tag:

  • <!doctype html>
  • <html lang="en">
  • <head>
  • <meta charset="utf-8" />
  • <title>
  • Downloading Text Using Blobs, URL.createObjectURL(), And The Anchor Download Attribute In JavaScript
  • </title>
  •  
  • <link rel="stylesheet" type="text/css" href="./demo.css" />
  • </head>
  • <body>
  •  
  • <h1>
  • Downloading Text Using Blobs, URL.createObjectURL(), And The Anchor Download Attribute In JavaScript
  • </h1>
  •  
  • <form>
  •  
  • <textarea name="input">Snakes. Why'd it have to be snakes?</textarea>
  •  
  • <!-- NOTE: Download attribute not supported in IE (but is in Edge). -->
  • <a href="javascript:void(0)" download="data.txt">
  • Download Text
  • </a>
  •  
  • </form>
  •  
  • <script type="text/javascript">
  •  
  • // Gather our DOM references.
  • var input = document.querySelector( "textarea[ name = 'input' ]" );
  • var download = document.querySelector( "a[ download ]" );
  •  
  • // In order to facilitate the download, we're going to allocate Object URLs.
  • // We'll need to keep track of those so we can manage the browser memory.
  • var downloadUrl = null;
  •  
  • // Listen for relevant form changes so that we can dynamically update the HREF
  • // attribute of our download link to contain the proper Object URL.
  • input.addEventListener( "input", updateDownloadHref, false );
  •  
  • // Initialize the download link.
  • updateDownloadHref();
  •  
  • // --------------------------------------------------------------------------- //
  • // --------------------------------------------------------------------------- //
  •  
  • // I update the HREF of the download link to point to the textarea payload.
  • function updateDownloadHref() {
  •  
  • // Create a binary representation of the plain-text input.
  • var blob = new Blob(
  • [ input.value ], // Blob parts.
  • {
  • type : "text/plain;charset=utf-8"
  • }
  • );
  •  
  • // When we create Object URLs, the browser will keep them in memory until the
  • // document is unloaded or until the URL is explicitly released. Since we are
  • // going to create a new URL every time the user hits a key-stroke (in this
  • // particular demo), we need to be sure to release the previous Object URL
  • // before we create the new one.
  • if ( downloadUrl ) {
  •  
  • URL.revokeObjectURL( downloadUrl );
  •  
  • }
  •  
  • // Create an addressable version of the blob.
  • // --
  • // CAUTION: At this point, the URL has been allocated and the blob will be
  • // kept in the document memory space until the document is unloaded or the
  • // URL is explicitly released (see above).
  • downloadUrl = URL.createObjectURL( blob );
  •  
  • // Tie the addressable version of the blob to the download link.
  • download.setAttribute( "href", downloadUrl );
  •  
  • console.group( "Object URL" );
  • console.log( "Text:", input.value );
  • console.log( "URL:", downloadUrl );
  • console.groupEnd();
  •  
  • }
  •  
  • </script>
  •  
  • </body>
  • </html>

For the sake of this demo, I'm creating a new Blob URI after every input event. That Blob URI is then attached to the "download" link. Which, when clicked, will start downloading the textarea content:


 
 
 

 
 Download text as Blob URI with URL.createObjectURL(). 
 
 
 

In the past, this is the kind of workflow that I would need some sort of server-side component in order to fulfill. But now, the client-side functionality is getting so robust that more and more processing can be offloaded to the browser. What an exciting time to be alive!



Looking For A New Job?

Ooops, there are no jobs. Post one now for only $29 and own this real estate!

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.