Providing a Client side download with JavaScript and HTML5

Share this: 

The web is changing, Offline web applications are getting popular rapidly. Crazy things are happening in your browser that were only possible on the server before. For example, you can create and manipulate images in your browser with HTML5 Canvas, create and manipulate SVG vectors, capture and manipulate video/audio from input devices with Web RTC. Cool, right?definitely cool! as you have a plenty of things you could probably want to save, Client side download is really essential. Let's go through the process how I managed to do it, limitations and how I took care of them. There may be better ways. some are still experimental (unfortunately, Browser support is still an issue) and some I may not know about, feel free to share your thoughts in the comments section. I'll explain some points on how the browser handles file types and some other things as well, I think you'll have fun.

I did the same thing while I was working on one of my open source projects, the Firefox OS app manifest generator, check out the application here, as it runs inside github, I had to find a way to offer client side download.

Mime types :

a mime type tells your browser what type of content it's receiving. for example, html pages have a mime type of text/html, png images have a type of image/png and so on. so every different type of file has different mime types. Web browsers decide what to do with a file by it's mime type. and it tries to open known mime types. for example, modern browsers are capable of opening html files, image files, audio/video files as well. if the server serves an image file, the browser opens it, rather than prompting you to save it. same goes for known audio video types. browsers tend to save (or prompt you with a download dialog) when it finds an unknown mime type.
I've used a mime type - application/text , cause browsers tend to show text/* files.

Data URI :

Data URI is used to represent data in the form of a URI, you've probably seen it in action with base64 encoded images. data URI's start with data: followed by the mime type, open up your browser and type

data:text/html,<h1>this is a html file generated in your address bar</h1>

cool! right, your browser successfully rendered it as html file. okay, now let's make the browser save the file instead of downloading it. type these in your addressbar:

data:application/text,hello world, saving file generated in your address bar

you should have a download dialog/file downloaded by now. open the file, and you may see what you've written, in the file. notice that the spaces are gone. this is a problem specific for text files only, because your text file contents wasn't encoded as URI,URI's represent spaces with a %20 some other special characters have different meaning as well, we'll fix this with JavaScript later on in this article.

File name :

you might have noticed that the file name is a random name or parts of your content, without extension. This is probably something you don't want, we'll be using the download attribute introduces in HTML5, to understand how it works, create a HTML file, and write these,

 <!DOCTYPE html>
 <html lang="en">
  <head>
   <meta charset="UTF-8">
   <title>Client side download</title>
  </head>
  <body>
     <a href="data:application/text,hello world" download="anam.txt">Click me</a>
  </body>
 </html>

the file contains only one a element, look at it's href attribute, our data URI is there. and it has a download attribute, that specifies the file name. open the file in your browser and click on the link, a file named, anam.txt should be downloaded. great! but the space problem is still there. that's what we need JavaScript for.

A full example

Let's create a HTML file with a text area and a download link, our goal is to download the contents of the text file when the user clicks the download link. The application will also prompt for a file name before downloading. Let's get started.

Let's modify our previous file and write this:

<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Client side download</title>
 </head>
 <body>
   <textarea id="content">type your text here</textarea><br>
   <a id="download" href="#" download="">Click me</a>
 </body>
</html> 

Let's add some JavaScript to make it functional, add a <script></script> tag and write these inside, follow the code comments to find out the mechanism :

 //get the dom element for download link
 var downloadLink = document.querySelector('#download');
 //run the code when user clicks the download link
 downloadLink.addEventListener('click',function(){
   //get the content of the text area
   var content = document.querySelector('#content').value;
   //encode the content and make it data URI
   var contentURI = 'data:application/manifest,'+encodeURIComponent(content);
   //set it as the href of the a element
   downloadLink.setAttribute('href',contentURI);
   //prompt for a file name or use download.txt as defaule
   var filename = prompt('Enter Filename') || 'download.txt';
   //set the filename as download attribute
   downloadLink.setAttribute('download',filename);
}); 

open the file with your browser. everything should be working.

You can use this approach to do a lot of client side magics. Use the comment section to express your thoughts.