Specifying blob encoding in Google Chrome

Viewed 48097

The following code (vendor normalized) works perfectly fine and displays "➀➁➂ Test" in Firefox 8, but displays "➀âžâž‚ Test" in Google Chrome. Is there any way to preserve encoding of blobs in Google Chrome short of writing a file to a temporary filesystem using the filesystem API?

var b = new Blob(["➀➁➂ Test"], {type: "text/plain;charset=UTF-8"});
var url = URL.createObjectURL(b);
open(url);
3 Answers

new Blob(["➀➁➂ Test"]) will generate a Blob representing that text encoded as UTF-8.

That browsers assumes text files should be read in ISO is a weird choice IMM.

Appending the { type: "text/plain;charset=utf8" } should generate the proper Content-Type header when they browsers will serve it through a blob URI. That Chrome doesn't with open() sounds like a bug.

Now you can workaround this by prepending a BOM sequence at the beginning of your text file, so that Chrome detects it as UTF, even without Content-Type info:

var BOM = new Uint8Array([0xEF,0xBB,0xBF]);
var b = new Blob([ BOM, "➀➁➂ Test" ]);
var url = URL.createObjectURL(b);
open(url);

var BOM = new Uint8Array([0xEF,0xBB,0xBF]);

var blob_BOM = new Blob([ BOM, "➀➁➂ Test" ]);
var url_BOM = URL.createObjectURL(blob_BOM);
// for demo we also create one version without BOM
var blob_noBOM = new Blob([ "➀➁➂ Test" ]);
var url_noBOM = URL.createObjectURL(blob_noBOM);

document.querySelector('.BOM').href = url_BOM;
document.querySelector('.no-BOM').href = url_noBOM;

// to check whether they contain the same data, apart from the BOM
(async() => {
  const buf_BOM = await blob_BOM.slice(3).arrayBuffer(); // remove BOM sequence
  const buf_noBOM = await blob_noBOM.arrayBuffer();
  
  console.log( 'with BOM text data:' );
  console.log( JSON.stringify( [...new Uint8Array( buf_BOM )] ) );
  console.log( 'without BOM text data:' );
  console.log( JSON.stringify( [...new Uint8Array( buf_noBOM )] ) );

})();
<a class="BOM">open file with BOM</a><br>
<a class="no-BOM">open file without BOM</a>

Gecko (Firefox), WebKit (Safari, Chrome) and Opera support the non-standard btoa function for encoding a string in base 64. In order to get a base 64 string containing a string encoded as UTF-8 you need to use the encodeURIComponent-unescape trick. encodeURIComponent encodes a string as UTF-8 URL but unescape decodes each %xx as a single character. btoa expects a binary string of whatever encoding you want.

var base64 = btoa(unescape(encodeURIComponent(data)));
window.open("data:text/plain;charset=UTF-8;base64,"+base64,"UTF-8 Text");

Of course this does not work in IE, but I think IE 10 will support the Blob-API. Who knows how it will handle encodings.

PS: IE seems not to be able to window.open data:-urls and would have a ridiculous small url length limitation anyway.

PPS: This works for me in Chrome:

var b = new Blob(["➀➁➂ Test"],{encoding:"UTF-8",type:"text/plain;charset=UTF-8"});
var url = URL.createObjectURL(b);
window.open(url,"_blank","");
Related