I need a solution with encoding utf to 8-bit cp1251 or cp1252 using blob
I managed to change the
https://github.com/b4stien/js-csv-encoding including windows 1251, but there are insoluble problems:
Unfortunately noscript does not allow loading external javascript on a page with scripts turned off via it.
Therefore, it is impossible to use js-csv-encoding in the bookmarker, as well as to load jquery! Disabling noscript, especially after meltdown and specter is simply not secure.
Therefore, only the version of a small script written in native javascript is left.
If you find an alternative way to run jquery with noscript off, then finding a solution will be easier although I doubt it's possible.
A good solution would be
https://www.npmjs.com/package/windows-1251 or https://www.npmjs.com/package/windows-1252
However, it does not succeed to transcode two-byte text into a single-byte text through these scripts. For example:
<script src="windows-1251.js"></script>
<script type="text/javascript">
function download(text, name, type) {
var a = document.getElementById("a");
var file = new Blob([text], {type: type});
a.href = URL.createObjectURL(file);
a.download = name;
</script>
There have been many attempts to use windows1251, for example these:
<script type="text/javascript">
function exportToCsv() {
window.open(windows1251.encode('data:text/csv;charset=windows-1251,' +'текст'));
}
var button = document.getElementById('b');
button.addEventListener('click', exportToCsv);
</script>
<script type="text/javascript">
function exportToCsv() {window.open('data:text/csv;charset=windows-1251,' +windows1251.encode('текст'));}
var button = document.getElementById('b');
button.addEventListener('click', exportToCsv);
</script>
Using encode or decode from windows-1251 does not translate the script into a 8-bit format. In js-csv-encoding, csvContentEncoded is used for transcoding:
Attempts to use something like that have failed. Perhaps you need some kind of hack, just put windows-1251 is not enough,
since js stores in utf8, then most likely you need to add the conversion to 1251 at the very end. Part of the code: js-csv-encoding.
var csvContent = 'текст',
textEncoder = new CustomTextEncoder('windows-1251', {NONSTANDARD_allowLegacyEncoding: true}),
fileName = 'some-data.csv';
var a = document.getElementById('download-csv');
a.addEventListener('click', function(e) {
var csvContentEncoded = textEncoder.encode([csvContent]);
var blob = new Blob([csvContentEncoded], {type: 'text/csv;charset=windows-1251;'});
saveAs(blob, fileName);
e.preventDefault();
});
I also tried to use conversions using charcode, saving not to the server but to the computer, so using urlencode .. is not the right solution, because in this case I have to encode the text into the readable one.
Of course, it's hard to find a solution of no more than 4000-5000 characters for a bookmarklet, and my knowledge is not enough.
If there is a solution with the help of other scripts, for example, recoding by the value table, this can also be a solution.
I spent the half of the day trying to save an xml file with Cyrillic symbols in windows-1251 encoding. Turned out it's pretty easy - you just need to create an appropriate byte array. See the example below (The full repo with this example):
import iconv from 'pika-iconv-lite';
import saveAs from 'save-as';
const byteArrayWin1251 = iconv.encode(
`<?xml version="1.0" encoding="windows-1251"?>
<note>
<to>Михаил</to>
<from>Андрей</from>
<heading>Reminder</heading>
<body>Вот такая вот xml! И сохранюсь я как win-1251</body>
</note>`,
'win1251'
);
const blob = new Blob([byteArrayWin1251], { type: 'application/xml;charset=windows-1251' })
saveAs(blob, 'myxml.xml');
Related
This question already has answers here:
How to create a file in memory for user to download, but not through server?
(22 answers)
Closed 2 years ago.
I have data that I want to write to a file, and open a file dialog for the user to choose where to save the file. It would be great if it worked in all browsers, but it has to work in Chrome. I want to do this all client-side.
Basically I want to know what to put in this function:
saveFile: function(data)
{
}
Where the function takes in data, has the user select a location to save the file, and creates a file in that location with that data.
Using HTML is fine too, if that helps.
A very minor improvement of the code by Awesomeness01 (no need for anchor tag) with addition as suggested by trueimage (support for IE):
// Function to download data to a file
function download(data, filename, type) {
var file = new Blob([data], {type: type});
if (window.navigator.msSaveOrOpenBlob) // IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
else { // Others
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
}
Tested to be working properly in Chrome, FireFox and IE10.
In Safari, the data gets opened in a new tab and one would have to manually save this file.
function download(text, name, type) {
var a = document.getElementById("a");
var file = new Blob([text], {type: type});
a.href = URL.createObjectURL(file);
a.download = name;
}
click here to download your file
<button onclick="download('file text', 'myfilename.txt', 'text/plain')">Create file</button>
And you would then download the file by putting the download attribute on the anchor tag.
The reason I like this better than creating a data url is that you don't have to make a big long url, you can just generate a temporary url.
This project on github looks promising:
https://github.com/eligrey/FileSaver.js
FileSaver.js implements the W3C saveAs() FileSaver interface in
browsers that do not natively support it.
Also have a look at the demo here:
http://eligrey.com/demos/FileSaver.js/
Choosing the location to save the file before creating it is not possible. But it is possible, at least in Chrome, to generate files using just JavaScript. Here is an old example of mine of creating a CSV file. The user will be prompted to download it. This, unfortunately, does not work well in other browsers, especially IE.
<!DOCTYPE html>
<html>
<head>
<title>JS CSV</title>
</head>
<body>
<button id="b">export to CSV</button>
<script type="text/javascript">
function exportToCsv() {
var myCsv = "Col1,Col2,Col3\nval1,val2,val3";
window.open('data:text/csv;charset=utf-8,' + escape(myCsv));
}
var button = document.getElementById('b');
button.addEventListener('click', exportToCsv);
</script>
</body>
</html>
For latest browser, like Chrome, you can use the File API as in this tutorial:
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(window.PERSISTENT, 5*1024*1024 /*5MB*/, saveFile, errorHandler);
function SaveBlobAs(blob, file_name) {
if (typeof navigator.msSaveBlob == "function")
return navigator.msSaveBlob(blob, file_name);
var saver = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
var blobURL = saver.href = URL.createObjectURL(blob),
body = document.body;
saver.download = file_name;
body.appendChild(saver);
saver.dispatchEvent(new MouseEvent("click"));
body.removeChild(saver);
URL.revokeObjectURL(blobURL);
}
Tried this in the console, and it works.
var aFileParts = ['<a id="a"><b id="b">hey!</b></a>'];
var oMyBlob = new Blob(aFileParts, {type : 'text/html'}); // the blob
window.open(URL.createObjectURL(oMyBlob));
You cannot do this purely in Javascript. Javascript running on browsers does not have enough permission yet (there have been proposals) due to security reasons.
Instead, I would recommend using Downloadify:
A tiny javascript + Flash library that enables the creation and download of text files without server interaction.
You can see a simple demo here where you supply the content and can test out saving/cancelling/error handling functionality.
For Chrome and Firefox, I have been using a purely JavaScript method.
(My application cannot make use of a package such as Blob.js because it is served from a special engine: a DSP with a WWWeb server crammed in and little room for anything at all.)
function FileSave(sourceText, fileIdentity) {
var workElement = document.createElement("a");
if ('download' in workElement) {
workElement.href = "data:" + 'text/plain' + "charset=utf-8," + escape(sourceText);
workElement.setAttribute("download", fileIdentity);
document.body.appendChild(workElement);
var eventMouse = document.createEvent("MouseEvents");
eventMouse.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
workElement.dispatchEvent(eventMouse);
document.body.removeChild(workElement);
} else throw 'File saving not supported for this browser';
}
Notes, caveats, and weasel-words:
I have had success with this code in both Chrome and Firefox clients running in Linux (Maipo) and Windows (7 and 10) environments.
However, if sourceText is larger than a MB, Chrome sometimes (only sometimes) gets stuck in its own download without any failure indication; Firefox, so far, has not exhibited this behavior. The cause might be some blob limitation in Chrome. Frankly, I just don't know; if anybody has any ideas how to correct (or at least detect), please post. If the download anomaly occurs, when the Chrome browser is closed, it generates a diagnostic such as
This code is not compatible with Edge or Internet Explorer; I have not tried Opera or Safari.
StreamSaver is an alternative to save very large files without having to keep all data in the memory.In fact it emulates everything the server dose when saving a file but all client side with service worker.
You can either get the writer and manually write Uint8Array's to it or pipe a binary readableStream to the writable stream
There is a few example showcasing:
How to save multiple files as a zip
piping a readableStream from eg Response or blob.stream() to StreamSaver
manually writing to the writable stream as you type something
or recoding a video/audio
Here is an example in it's simplest form:
const fileStream = streamSaver.createWriteStream('filename.txt')
new Response('StreamSaver is awesome').body
.pipeTo(fileStream)
.then(success, error)
If you want to save a blob you would just convert that to a readableStream
new Response(blob).body.pipeTo(...) // response hack
blob.stream().pipeTo(...) // feature reference
Javascript has a FileSystem API. If you can deal with having the feature only work in Chrome, a good starting point would be: http://www.html5rocks.com/en/tutorials/file/filesystem/.
According to caniuse the download attribute of <a> element is supported at Microsoft Edge build 10547+, but not IE or Safari.
How to download a file object without using <a> element with download attribute set or a server?
There are a number of ways of triggering a download. Following are a few:
Use a form:
<form method="get" action="mydoc.doc">
<button type="submit">Download</button>
</form>
Use javascript:
<button type="submit" onclick="window.open('mydoc.doc')">Download</button>
Although I support #LeoFarmer's answer, I would like to offer two "hackable" approaches:
If the file is very small, you can use a with the href='data:[<mediatype>][;base64],<data>'.
This could allow you to add content disposition in the mediatype, emulating an HTTP header. This hack is also not as portable as one might hope.
On small to medium files, it's possible to download the file using AJAX, and then use the Javascript File API to prompt for file saving (the API doesn't support saving, but it's easy to convert the data to a data URL).
If you want to avoid the Javascript File API, you can try emulating an anchor click, as suggested here.
Again, as pointed out by Leo Farmer, these solutions can't promise that the browser won't open the file in a new tab instead of saving it to the disk, but I think it's safe to say that all users will be able to gracefully degrade to a cmd+S or ctrl+S keyboard shortcut :-)
You may do this using both download attribute and jquery. download attribute don't support in ie and safari/ios. So you may use jquery to do that
$('.download').click(function(e) {
e.preventDefault(); //stop the browser from following
window.location.href = 'uploads/file.doc';
});
Download
Use FileSaver.js
It supports all the commonly used browsers.
Just include:
<script type="text/javascript" src="FileSaver.min.js"></script>
and use it like:
var file = new File(["Hello, world!"], "hello world.txt", {type: "text/plain;charset=utf-8"});
saveAs(file);
Note:
To make it work also in Safari < 6, Opera < 15 and FireFox < 20 you need to include Blob.js as a dependency.
You can use data URI data:[<mediatype>][;base64],<data> representation of file either created manually or utilizing FileReader(), .readAsDataURL(), with MIME type set to application/octet-stream, encodeURIComponent(), window.open()
<script>
var file = "data:application/octet-stream,"
+ encodeURIComponent("<!DOCTYPE html>"
+ "<html><body>"
+ "<div>abc</div>"
+ "</body></html>");
var saveFile = window.open(file, "_self");
</script>
<script>
var blob = new Blob(["abc"], {type:"text/plain"});
var reader = new FileReader();
reader.addEventListener("load", function(e) {
// replace existing `MIME` type with `application/octet-stream`
var file = "data:application/octet-stream;"
+ e.target.result.split(/;/)[1];
var saveFile = window.open(file, "_self");
});
reader.readAsDataURL(blob)
</script>
plnkr http://plnkr.co/edit/IS1OC0laRwL3BuuF9zay?p=preview
If you're using server-side then follow the form submission mechanism to render the page.
In MVC we can use below code
Html
#using (Html.BeginForm("GetAttachment", "User", FormMethod.Post))
{
<button type="submit">Download</button>
}
MVC Controller
public ActionResult GetAttachment()
{
string filename = "File.pdf";
string filepath = AppDomain.CurrentDomain.BaseDirectory + "/Path/To/File/" + filename;
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = MimeMapping.GetMimeMapping(filepath);
var cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = true,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(filedata, contentType);
}
I have hacked together a small tool to extract shipping data from Amazon CSV order data. it works so far. here is a simple version as JS Bin: http://output.jsbin.com/jarako
For printing stamps/shipping labels, I need a file for uploading to Deutsche Post and to other parcel services. I used a small function saveTextAsFile which i found on stackoverflow. Everything good so far. No wrong displayed special characters (äöüß...) in the output textarea or downloaded files.
All these german post / parcel services sites accept only latin1 / iso-8859-1 encoded files for upload. But my downloaded file is always utf-8. If i upload it, all special characters (äöüß...) go wrong.
How can i change this? I still searched a lot. I have tried i.e.:
Setting the charset of the tool to iso-8859-1:
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
But the result is: Now I have wrong special characters still in the output textarea and in the downloaded file. If I upload it to the post site, I still get more wrong characters. Also if I check the encoding in CODA Editor it still says the downloaded file is UTF-8.
The saveTextAsFile function uses var textFileAsBlob = new Blob([textToWrite], {type:'text/plain'});. May be there is a ways to set the charset for download there!?
function saveTextAsFile()
{
var textToWrite = $('#dataOutput').val();
var textFileAsBlob = new Blob([textToWrite], {type:'text/plain'});
var fileNameToSaveAs = "Brief.txt";
var downloadLink = document.createElement("a");
downloadLink.download = fileNameToSaveAs;
downloadLink.innerHTML = "Download File";
if (window.webkitURL != null)
{
// Chrome allows the link to be clicked
// without actually adding it to the DOM.
downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
}
else
{
// Firefox requires the link to be added to the DOM
// before it can be clicked.
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
downloadLink.onclick = destroyClickedElement;
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
}
downloadLink.click();
}
Anyhow, there have to be a way to download files in other encoding as the site uses itself. The Amazon site, where i download the CSV file from is UTF-8 encoded. But downloaded CSV file from there is Latin1 (iso-8859-1) if i check it in CODA...
SCROLL DOWN TO THE UPDATE for the real solution!
Because I got no answer, I have searched more and more. It looks like there is NO SOLUTION in Javascript. Every test download I'v made, which was generated in javascript was UTF-8 encoded. Looks like Javascript is only made for UNICODE / UTF-8 or an other encoding would (possibly) only apply if the data would be transported again using a former HTTP transport. But for a Javascript, which runs on the client no additional HTTP transport happens, because the data is still on the client..
I have helped me now with building a small PHP Script on my server, to which i send the Data via GET or POST request. It converters the encoding to latin1 / ISO-8859-1 and downloads it as file. This is a ISO-8859-1 file with correctly encoded special characters, which I can upload to the mentioned postal and parcel service sites and everything looks good.
latin-download.php: (It is VERY IMPORTANT to save the PHP file itself also in ISO-8859-1, to make it work!!)
<?php
$decoded_a = urldecode($_REQUEST["a"]);
$converted_to_latin = mb_convert_encoding($decoded_a,'ISO-8859-1', 'UTF-8');
$filename = $_REQUEST["filename"];
header('Content-Disposition: attachment; filename="'.$filename.'"; content-type: text/plain; charset=iso-8859-1;');
echo $converted_to_latin;
?>
in my javascript code i use:
<a id="downloadlink">Download File</a>
<script>
var mydata = "this is testdata containing äöüß";
document.getElementById("downloadlink").addEventListener("click", function() {
var mydataToSend = encodeURIComponent(mydata);
window.open("latin-download.php?a=" + mydataToSend + "&filename=letter-max.csv");
}, false);
</script>
for bigger amounts of data you have to switch from GET to POST...
UPDATE 08-Feb-2016
A half year later now i have found a solution in PURE JAVASCRIPT. Using inexorabletash/text-encoding. This is a polyfill for Encoding Living Standard. The standard includes decoding of old encodings like latin1 ("windows-1252"), but it forbids encoding into these old encoding types. So if you use the browser implemented window.TextEncoder function it does offer only UTF encoding. BUT, the polyfill solution offers a legacy mode, which does ALLOW also encoding into old encodings like latin1.
i use it like that:
<!DOCTYPE html>
<script>
// 'Copy' browser build in TextEncoder function to TextEncoderOrg (because it can NOT encode windows-1252, but so you can still use it as TextEncoderOrg() )
var TextEncoderOrg = window.TextEncoder;
// ... and deactivate it, to make sure only the polyfill encoder script that follows will be used
window.TextEncoder = null;
</script>
<script src="lib/encoding-indexes.js"></script> // needed to support encode to old encoding types
<script src="lib/encoding.js"></script> // encording polyfill
<script>
function download (content, filename, contentType) {
if(!contentType) contentType = 'application/octet-stream';
var a = document.createElement('a');
var blob = new Blob([content], {'type':contentType});
a.href = window.URL.createObjectURL(blob);
a.download = filename;
a.click();
}
var text = "Es wird ein schöner Tag!";
// Do the encoding
var encoded = new TextEncoder("windows-1252",{ NONSTANDARD_allowLegacyEncoding: true }).encode(text);
// Download 2 files to see the difference
download(encoded,"windows-1252-encoded-text.txt");
download(text,"utf-8-original-text.txt");
</script>
The encoding-indexes.js file is about 500kb big, because it contains all the encoding tables. Because i need only windows-1252 encoding, for my use i have deleted the other encodings in this file. so now there are only 632 byte left.
The problem is not the encoding but the fact that the special characters are displayed wrong in some applications, e.g. Microsoft Excel. UTF-8 is fine for displaying all special German characters. You can fix the problem by adding a Byte order mark (BOM) in front of the csv.
const BOM = "\uFEFF"
let csvData = BOM + csvData
const blob = new Blob([csvData], { type: "text/csv;charset=utf-8" });
Solution based on this github post
You cannot force a web server to send you data in a given encoding, only ask it politely. Your approach to just convert to the format you need is the right way to go.
If you wanted to avoid the PHP script, you may have luck specifying the encoding as a parameter when creating your Blob:
var textFileAsBlob = new Blob(textToWrite, {
type: 'text/plain;charset=ISO-8859-1',
encoding: "ISO-8859-1"
});
See Specifying blob encoding in Google Chrome for more details.
This question already has answers here:
How to create a file in memory for user to download, but not through server?
(22 answers)
Closed 6 years ago.
Typically, HTML pages can have link to documents (PDF, etc...) which can be downloaded from the server.
Assuming a Javascript enabled webpage, is it possible to dynamically create a text document (for example) from within the user browser and add a link to download this document without a round trip to the server (or a minimal one)?
In other word, the user would click on a button, the javascript would generate randoms numbers (for example), and put them in a structure. Then, the javascript (JQuery for example) would add a link to the page to download the result as a text file from the structure.
This objective is to keep all (or at least most) of the workload on the user side.
Is this feasible, if yes how?
Here's a solution I've created, that allows you to create and download a file in a single click:
<html>
<body>
<button onclick='download_file("my_file.txt", dynamic_text())'>Download</button>
<script>
function dynamic_text() {
return "create your dynamic text here";
}
function download_file(name, contents, mime_type) {
mime_type = mime_type || "text/plain";
var blob = new Blob([contents], {type: mime_type});
var dlink = document.createElement('a');
dlink.download = name;
dlink.href = window.URL.createObjectURL(blob);
dlink.onclick = function(e) {
// revokeObjectURL needs a delay to work properly
var that = this;
setTimeout(function() {
window.URL.revokeObjectURL(that.href);
}, 1500);
};
dlink.click();
dlink.remove();
}
</script>
</body>
</html>
I created this by adapting the code from this HTML5 demo and messing around with things until it worked, so I'm sure there are problems with it (please comment or edit if you have improvements!) but it's a working, single-click solution.
(at least, it works for me on the latest version of Chrome in Windows 7)
By appending a data URI to the page, you can embed a document within the page that can be downloaded. The data portion of the string can be dynamically concatenated using Javascript. You can choose to format it as a URL encoded string or as base64 encoded. When it is base64 encoded, the browser will download the contents as a file. You will have to add a script or jQuery plugin to do the encoding. Here is an example with static data:
jQuery('body').prepend(jQuery('<a/>').attr('href','data:text/octet-stream;base64,SGVsbG8gV29ybGQh').text('Click to download'))
A PDF file? No. A txt file. Yes. With the recent HTML5 blob URIs. A very basic form of your code would look something like this:
window.URL = window.webkitURL || window.URL;
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
var file = new window.BlobBuilder(),
number = Math.random().toString(); //In the append method next, it has to be a string
file.append(number); //Your random number is put in the file
var a = document.createElement('a');
a.href = window.URL.createObjectURL(file.getBlob('text/plain'));
a.download = 'filename.txt';
a.textContent = 'Download file!';
document.body.appendChild(a);
You can use the other methods mentioned in the other answers as a fallback, perhaps, since BlobBuilder probably isn't supported very well.
Demo
Note: BlobBuilder seems to be deprecated. Refer to this answer to see how to use Blob instead of BlobBuilder. Thanks to #limonte for the heads up.
I have an image encoded in base64 in a javascript variable : data:image/png;base64, base64 data
[EDIT]
I need to save that file to disk without asking to the visitor to do a right click
[/EDIT]
Is it possible ? How ?
Thanks in advance
Best regards
I know this question is 2 years old, but hopefully people will see this update.
You can prompt the user to save an image in a base64 string (and also set the filename), without asking the user to do a right click
var download = document.createElement('a');
download.href = dataURI;
download.download = filename;
download.click();
Example:
var download = document.createElement('a');
download.href = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
download.download = 'reddot.png';
download.click();
In order to trigger a click event using Firefox, you need to do what it is explained in this SO answer. Basically:
function fireEvent(obj,evt){
var fireOnThis = obj;
if(document.createEvent ) {
var evObj = document.createEvent('MouseEvents');
evObj.initEvent( evt, true, false );
fireOnThis.dispatchEvent( evObj );
} else if( document.createEventObject ) {
var evObj = document.createEventObject();
fireOnThis.fireEvent( 'on' + evt, evObj );
}
}
fireEvent(download, 'click')
As of 20/03/2013, the only browser that fully supports the download attribute is Chrome. Check the compatibility table here
... without asking to the visitor anyhing ... Is it possible?
No, that would have been a security hole. If it was possible, one would be able to write malware to the enduser's disk unaskingly. Your best bet may be a (signed) Java Applet. True, it costs a bit of $$$ to get it signed (so that it doesn't pop security warnings), but it is able to write data to enduser's disk without its permission.
I am surprised nobody here mentioned using HTML5 blobs together with a couple of nice libraries.
You first need https://github.com/eligrey/FileSaver.js/ and https://github.com/blueimp/JavaScript-Canvas-to-Blob.
Then you can load the image into a canvas
base_image = new Image();
base_image.src ='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
the canvas into a blob
var canvas = document.getElementById('YourCanvas');
context = canvas.getContext('2d');
// Draw image within
context.drawImage(base_image, 0,0);
and finally save it
x_canvas.toBlob(function(blob) {
saveAs(blob, "screenshot.png");
}, "image/png");
FF is not fully supported but at least you get a separate page with the image.
Check this out: http://jsfiddle.net/khhmm/9/
EDIT: this is not compatible with Safari / Mac.
As other answers already stated, you cannot do it only with javascript. If you want, you can send the data (using normal HTTP POST) to a PHP script, call header('Content-type: image/png') and output the decoded image data to the page using echo base64_decode($base64data).
This will work just as if user clicked on an image and open it or prompt him to save the file to disk (the normal browser's save file dialog).
It's not possible.
If it was, browsers would be massively insecure, being able to write random data to your hard disk without user interaction.
with javascript, you can't. the only real possibility i can think of will be a java-applet, but maybe (i don't know how long that image should be saved) you could simply add an img-tag with you png and force caching (but if the user deletes his cache, the image will be gone).
I think it's possible with JavaScript if you use ActiveX.
Another possibility is to make the server spit out that file with a different mime type so the browser asks the user to save it.
I think you can do it something(maybe not only with javascript...xul programming needed). There are Firefox addons that save images to a folder(check Firefox addons site)
You can make this file as blob on the server and use setTimeout function in order to fire the download.
The accepted solution seems to have a limitation for large data. If you're running into this (instead of the downloaded file's name, I see "download" and "Failed - Network error" in Chrome), here's what I did in order to download a 2mb file:
const blob = await (await fetch(document.getElementById('canvasID').toDataURL())).blob();
const file = new File([blob], {type:"image/png", lastModified: new Date()});
var a = document.createElement('a');
a.href = window.URL.createObjectURL(file);
a.download = 'image.png';
a.click();