My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.
Showing posts with label download. Show all posts
Showing posts with label download. Show all posts

Thursday, August 18, 2011

HTML5: How To Create Downloads On The Fly

this is a quick one I have implemented already in fuckn.es in the create angry memory button logic ...

The New Download Attribute

Hopefully soon, most updated browser will implement the download attribute in hypertext links (aka: <a> tag)
The quick summary is this one:
The download attribute, if present, indicates that the author intends the hyperlink to be used for downloading a resource. The attribute may have a value; the value, if any, specifies the default filename that the author recommends for use in labeling the resource in a local file system.

And this is a basic example:
click here to
<a
    href="resource234.txt"
    download="license.txt"
>
    download the license
</a>


What Is Download For

Well, I am pretty sure you have read at least once in your life this kind of extra info beside a link:
right click to download the content and "Save As ..."
Moreover, I am pretty sure you have created at least once in your server a page able to force a generic file download.
All these instructions and server side headers/files may disappear thanks to this new attribute, also because there's no such thing as "right button" on touch screens, neither in some newer device pointer.
If the file is meant to be download, it will ... cool?

Create Downloads On The Fly Via JavaScript

When I have read about it, I have instantly realized the potentials of this attribute combined with inline data uri scheme.

Only HTML5 ?

Nope! Please note that many browsers let us already practice this technique. Some may open the file in a new blank page while some other may download directly the file as is for Chrome and the CSV example.
As graceful degradation, the "right click" procedure will still do the trick.

Downlaod Canvas As Image Example

First example is a classic one: how to save a canvas snapshot as image via "click".
// basic example
function createDownloadLink(canvas, name) {
    var a = document.createElement("a");
    a.download = name;
    a.title = "download snapshot";
    a.href = canvas.toDataURL();
    return a;
}

// some paragraph in the page
document.querySelector(
    "p.snapshot"
).appendChild(createDownloadLink(
    document.querySelector("#game"),
    "snapshot" + (-new Date) + ".png"
));

When the user will tap/click on the link, the browser will simply start the download. No server side involved at all!

Save A Page As PDF

Thanks to this technique we may use same trick to produce a PDF file out of whatever web page.
// basic example
function createPDFLink(fileName) {
    var doc = new pdf();
    // whatever content you want to download
    var a = document.createElement("a");
    a.download = fileName;
    a.title = "download as PDF";
    a.href = doc.output('datauri',{"fileName":name});
    return a;
}

// some paragraph in the page
document.querySelector(
    "p.saveaspdf"
).appendChild(createPDFLink(
    "document-" + document.title + ".pdf"
));

Of course if the page content changes we can replace the old link with a freshly new created one.

Save Table As CSV

Well, another classic here, the csv format out of a table. This is a basic but working example ;)
<script>
// really basic example
function tableToCSV(table) {
    for (var
        header = table.querySelectorAll("tr th"),
        rows = table.querySelectorAll("tr td"),
        hlength = header.length,
        length = hlength + rows.length,
        result = Array(hlength),
        i = hlength,
        j;
        i < length; ++i
    ) {
        j = i % hlength;
        j || result.push("\n");
        result.push(rows[j].innerHTML);
        ++j % hlength && result.push(",");
    }
    i = 0;
    while (i < hlength) {
        result[i] = header[i].innerHTML + (
            ++i < hlength ? "," : ""
        );
    }
    return result.join("");
}
this.onload = function () {
    var a = document.body.appendChild(
        document.createElement("a")
    );
    a.download = "table.csv";
    a.href = "data:text/csv;base64," + btoa(
        tableToCSV(document.querySelector("table"))
    );
    a.innerHTML = "download csv";
};
</script>
<table>
    <tr>
        <th>name</th>
        <th>age</th>
    </tr>
    <tr>
        <td>Dan</td>
        <td>33</td>
    </tr>
    <tr>
        <td>John</td>
        <td>32</td>
    </tr>
</table>

Most likely we can test already above example even if the name won't probably be the chosen one.
For safe base64 encode, compatible with UTF-8 pages, have a look at this script ( base64.encode() and base64.decode() ).


Different developers asked already about compatibility.
As I have said before, we need to differentiate between "download" attribute compatibility AND inline data uri link compatibility.
In the first case I don't know browsers that will force the download with the specified name yet, but I'll update this section as soon as I know someone.
In the latter case, IE9, Chrome, Firefox, Safari, Webkit based, and Opera seem to be already compatible.
The main problem/limit I have spotted in fuckn.es is the size of the data uri, in certain cases we may need a decent/fast machine otherwise we may end up killing RAM and CPU performances.
IE8 is compatible as well except IE8 has limited data uri for CSS images, as example, and I expect same limit for this technique.
Bear in mind when all browsers will be compatible, we will still have data stream limit problem, or better, really big files has to be parsed on the fly in one shot, no "download progress" possibility.


As Summary

... now you know ... ;)

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:

data:[<mediatype>][;base64],<data>

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);
iframe.style.position = "absolute";
iframe.style.top = "-10000px";
document.documentElement.insertBefore(
iframe,
document.documentElement.firstChild
);
};

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.

Conclusion

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)