two months agoaddSizes.js: Snazzy automatic link file-size generation
Often in the development of a site I come across the need to display the size of a document next to the link targeting it. I also like to display the type of file the link targets, for example, when linking to pdfs, mp3s or Word documents.
These indications distinguish the 'attachment' link from a normal web link, whilst also giving the user some inkling of the time they will need to wait to view the resulting content.
So I was pretty excited when Simon bounded in from work and enthusiastically demonstrated json-head, a Google App Engine application he built on the train home.
Every file on the web, be it a web page, a text file or whatever has HTTP headers associated with it. This is meta information about the file that is sent before the actual contents of the file itself. Included in this meta information is the size of the document.
You can call json-head with JavaScript (JSONP). It takes the URL of a file on the web and performs a HEAD request against it to return the headers and not the actual content itself. The application then returns this information in JavaScript object notation (JSON) to a callback function you have written.
One of the first things that occurred to me was how this could be used to solve the problem of dynamically adding the file size to links. The resulting script I wrote is being used on this site, so before I explain how it works here's a quick demo of a pdf document and an mp3 file. For a further demo take a look at this basic file.
Using jQuery we first use CSS selectors to find all of the links with the relevant file extensions:
$('a[href$=".pdf"], a[href$=".doc"], a[href$=".mp3"], a[href$=".m4u"]')
For each link, we get the type of the target by splitting the href value on '.' and grabbing the last value. This is the file extension.
jQuery neatly abstracts the faff involved in using JSONP. We pass json-head the URL of the file we want to inspect and the name of the callback function we want it to run with the results. The callback function in this case is '?', which tells jQuery to create a random function name hooked up to the function we pass to getJSON().
var url= "http://json-head.appspot.com/?url="+encodeURI(this.href)+"&callback=?";
$.getJSON(url, function(json){ /* ... */ }
We can now inspect the information sent to us by json-head. If everything went OK we have the size of the file in bytes in the Content-Length header.
var length = parseInt(json.headers['Content-Length'], 10);
Once we have the exact size we need to do a bit more work to get human readable file sizes. We loop through an array to find the largest unit that fits. Once the right size has been found, the script breaks out of the loop storing the unit name and the length to 1 decimal place, so we end up with something like 1.2GB.
for(var i = 0; i < units.length; i++){
var unitSize = units[i][0];
var unitText = units[i][1];
if (length >= unitSize) {
length = length / unitSize;
// 1 decimal place
length = Math.ceil(length * 10) / 10;
var lengthUnits = unitText;
break;
}
}
Now the maths is over, all that remains is to insert the results back into the dom. I am using jQuery's .after() method but if you wanted to insert the size and type directly into the link you could change this to use .append()
I also add the type of the document as a class, to allow for additional styling such as adding an icon. A future version of the script will add the content type to the link as well.
link.after(' (' + type + ' ' + length + ' ' + lengthUnits + ')');
link.addClass(type);
To use this script in your site, simply include jQuery and then the addSizes script itself will do the rest of the magic.
PLEASE NOTE: this may not be 100% reliable due to App Engine being occasionally and unavoidably flakey.
UPDATE: Please see the update to addSizes.

Excellent idea! Wonder if it would be worth implementing a caching parameter for json-head to reduce connection overhead for the target server? Could also have a batch request format to call the json-head service with multiple files in one go. I love feature creep.
P.S. I like the new blog, but wish to lodge a formal complaint concerning the lack of an RSS feed.
Richard Terry 27th August 2008 21:03
Thanks Rich, yeah some sort of caching would be a good idea for json-head, I think Si was thinking of implementing that eventually (maybe). The multiple files at once is also an interesting one.
btw there is an RSS feed ... cunningly hidden in the bar at the top (not sure if that is a good idea or not yet). It is also listed on the about page, probs not obvious enough though!
Natalie 28th August 2008 00:23
Ahh, missed the bar! Nice, but could do with arrows or something? Thought it was just a design element. I was mostly confused though because firefox didn't detect the feed and show it in the address bar. Think there might still be a problem with the feed though - google reader showed the HTML source.
Richard Terry 29th August 2008 01:51
I was thinking of maybe showing it the first time and hiding it on subsequint visits through use of a cookie. That or show it on all pages but hide it on the homepage.
You are right, the feed has been intermitantly broaken, should be good now though. Thanks for spotting it not autodetecting the feed, will fix that :)
Natalie 30th August 2008 01:49
Great idea and a nice script. I think you should be using encodeURIComponent though, otherwise you won't be getting correct results for links that have an ampersand in them.
Marko Mrdjenovič 30th August 2008 14:42
Nice stuff. AppEngine is really starting to make headway as a services platform.
There needs to be a nice compiled list of these JSON services somewhere.
Adam 2nd September 2008 14:00
It is not the case that file size is always sent before the body. HTTP bodies can be chunked-encoded, in which case the file size is not specified before the body is sent.
Tim Olsen 2nd September 2008 16:13
Would love to see this as a mozilla ubiquity plugin :)
https://wiki.mozilla.org/Labs/Ubiquity/Ubiquity_0.1_User_Tutorial
Guy Fraser 2nd September 2008 16:38
Excellent work!
jesusvld 4th September 2008 15:59
Great idea. This will no doubt come in pretty handy and I'll give it a try. You guys really know your javascript. :)
Really like the new site by the way!
Ian 4th September 2008 18:31
Excellent. This is very useful, so thank you for sharing.
I do occasionally run in to a bug where some files are listed as being 20 bytes (when they're not) - one being a zip file, and one a pptx file (both of which I added to the list of css selectors in the javascript)
Nathan 5th September 2008 01:37
Now everything is showing up as 20 bytes... weird. Any idea on what could be causing that?
Nathan 8th September 2008 01:39
Ah just worked it out. The folder the site is in is password protected by the server, which prevents the sizes from being correctly generated.
Nathan 8th September 2008 02:00
Seriously sweet this! It's always a pain to add file sizes so anything that can automate is great.
Thank you very much for sharing this.
Erwin Heiser 12th September 2008 13:35
its really great time saver since so much of what we do requires links to different file types on our site. However, it does not seem to work for links that are relative. Is there a solution to this?
kelly p 12th September 2008 22:33
love the script, works like a charm for remote links.
Was wondering if anyone can think of a work around for links that are in a secure folder...
Even when a user is logged in and the file is availble to download the link size doesn't display. Im assuming because the files are in a secure folder.
any help would be appreciated
chris g 14th September 2008 00:03
kelly p
for a relative link solution try phps built in filesize() function.
chris g 14th September 2008 01:50
thanks for come up with this cool script !
a joomla 1.0 & 1.5 plugin implementation with this javascript can be found at VivoCiti, http://vivociti.com/content/view/64/53/
sunwu 19th September 2008 15:57