When I first started to put this demo together, I was operating under the assumption that all Data URIs had to be Base64-encoded. As such, I started to look at how one might convert plain text into a Base64 payload. It was then that I discovered that modern browsers provide btoa() and atob() methods specifically for Base64 encoding and decoding, respectively.
But then, when I went to double-check the Data URI syntax on MDN, I saw that they had examples that completely omitted the "base64" directive. As it turns out, you only need to Base64-encode non-text payloads, like images. If you need a Data URI that points to a plain text payload, all you have to do is encode the value the same way that you would encode any other URI component.
To pull both of these new findings together and cement them in my mental model, I created a small demo in which you can enter arbitrary text into a Textarea form field. The entered text value can then be downloaded as a .txt file using an anchor tag's href and download attributes. And, to make things more interesting, I added a checkbox that determines whether or not the Data URI in the href attribute is generated using a Base64 or a plain-text encoding.
NOTE: As a reminder, the "download" attribute of the Anchor instructs the browser to download the associated URL instead of navigating to it. It is supported in all modern browsers (including Edge).
As you can see, there's not too much going on in this demo. You enter some text, you click the anchor tag, you get prompted to download the text file (all on the client-side). It's definitely cool; but, the most interesting part of this demo is the updateDownloadHref() function that actually generates the Data URI for the download. Depending on the state of the checkbox, it either generates a Base64-encoded payload or a plain-text payload.
If we open the browser, enter some text in the textarea, and click the "Download Text" link, the browser will prompt us to save the file. And, if we open the file, we can see that it contains the text we entered in the browser:
As you can see, the "Download Text" link caused my Chrome browser to download the "data.txt" file. And, when I open the .txt file in Sublime Text, the contents of the file mirror what we had in the Textarea input field.
Want to use code from this post? Check out the license.
Thanks so much for this article! This technique immediately solved a major problem I was having with a tool at Lottery Post.
Now, using this technique, for any set of combinations greater than 100,000 lines, it shows a download button instead.
Because of the size of the downloads, I had to use a Blob technique instead of using a data URL directly. (A data URL can not handle huge sizes.) So the data URL is converted into a Blob object, and then the Blob is converted into a URL.
I am so grateful that you documented this, I had no idea this technique existed!
I'm glad you found this helpful :D And, I have to say, that I'm actually very interested in your "Blob" approach. Someone on my team just mentioned to me that the Data URI has a limitation (as you mentioned). And that they were using the Blob approach. It's something I'll have to take a look at as well.
I used the following function to convert the data URL into a Blob:
Then, I load up the link's href with some jQuery:
$downloadButton.attr( "href", URL.createObjectURL( dataUriToBlob( "data:text/plain;charset=utf-8," + evt.data.output + suffix ) ) );
(The "evt.data.output + suffix" is the output from the combinations generator.)
Very cool! I've never used the
Uint8Arraytype before. Thanks for pointing me in the right direction.
I forgot to mention that if your data URI is base64 encoded, you would need to change the first var line to:
Also, the data URI should not be URLEncoded. (Or you would need to add URL decoding to the first var.)
Gotcha. Though, I assume if I was planning to use a a Blob, I would bypass the original Data URI to begin with and just use the Blob stuff direclty.
Unfortunately, most major browsers have blocked top-frame navigation to data URLs. Apparently, they are commonly used in phishing attacks.
In Chrome's Dev Console, you'll see errors such as this:
Very interesting. I am not sure that I've run into this issue. Perhaps there are certain circumstances under which this security is applied; and, times when it is not? Thank you for pointing it out, though - I will keep my eyes open for this limitation.