My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Sunday, November 15, 2009

Inline Downloads with Data URLs

A quick post about another silly idea ...
With Data URLs we can incorporate images in layout or CSS.
The schema is really simple:


Since we need to specify a mediatype we could play around creating something unexpected ;-)

My Silly Idea

If we select something in a web page we can perform different actions via right click. So far so good ... but one thing we are missing, at least in Firefox, is a "Save As" option. If we want bring a piece of code, text, something else, into another software or editor we need to select, right click, copy, open or find the editor, right click, paste.
The ultra skilled developers goes well with ctrl+c and ctrl+v but there are still 3 operations to do: copy, find the destination, paste
What about making possible to simply save that part and go on reading or surfing in order to do not distract too much our lecture and review eventually later that piece of text or code?

Firefox Inline Download

function download(text){
// Yet Another Silly WebReflection Experiment
var iframe = document.createElement("iframe");
iframe.src = "data:application/octet-stream;base64," + btoa(text); = "absolute"; = "-10000px";

download("Hello World :-)");

If we have configured Firefox to ask us where to save files, we can even choose the name. Being the inline data protocol that simple, unfortunately I could not find a way to name the file. The concept in any case is simple, we could create a bookmark or a link able to save the selected text, if any.
In this case we select, and with a click we can directly organize the content in a named file or we can open it with the editor that will be probably the first option in the Open With question.

Side Effects

Well, the first one is that I am not even sure if this could be considered a security problem, and I am testing in Firefox 3.6 beta 2 (so I am not even sure this is possible with other versions).
We cannot remove the iframe until the user has saved the content and never before the save dialog will be showed, otherwise a generic onload will remove the content before Firefox can understand what to do.
On the other hand, since the content will be inline, the "Open With" should always work 'cause Firefox, which is a clever browser, saves and eventually remove, even if we cancel the operation.


Nobody will probably ever use above snippet, but I thought it was an interesting and new way to manipulate a technique used for totally different purposes (OK, I have to admit the only excuse I have is my new netbook and I had to test some code after Notepad++ installation :D)


Brett said...

I really like these tricks. It is working in Firefox 3.5.5 too... In IE8, after enabling ActiveX, I got an error about a connection attempt failing (then after refreshing, I didn't get any messages)...In Opera 10 and 10.01 and Chrome and nothing happened. Worked in Safari 4.0.3 though...

CG said...

Nice trick!

alsanan said...

Which netbook btw?

Andrea Giammarchi said...

@Brett IE can't work for both missed btoa function and iframe rather than <iframe><iframe/> in document create

@alsanan, Samsung N510, so far extremely happy about this choice ;)

Brett said...

Besides saving snippets, I can see uses in HTML-based IDEs (e.g., I've added your code to my very primitive and unfinished XML editor at -- I hope the likes of this can allow attribute editing and also be merged with a schema parser at some point) or even saving remote files made available via Ajax. Yes, the latter saving of files can be done with server-side code already, but it could be convenient for a file browser to automatically make the pages of a site easily available for download via its own interface (e.g. Gopher-style, as my column browser also at the above site aims to do).

Robbert Broersma said...

From the Xopus codebase, base64 encoding for IE:

function encode(msg)
if (typeof btoa == 'function')
return btoa(msg)

var buffer = [],
padCount = msg.length % 3,
padding = ['', '==', '='][padCount],
str = msg + ['', '\0\0', '\0'][padCount],
base64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('')

for (var n, i = 0, l = str.length; i < l;) {
n = (str.charCodeAt(i++) << 16)
+ (str.charCodeAt(i++) << 8)
+ str.charCodeAt(i++)

base64[n >> 18 & 63],
base64[n >> 12 & 63],
base64[n >> 6 & 63],
base64[n & 63]

return buffer.join('').substring(0, buffer.length - padding.length) + padding

Jani Hartikainen said...

I believe this technique has been used in multiple Opera widgets to provide the ability to download or store data from the widget on the hard drive