Why is readAsBinaryString() deprecated - javascript

Why is readAsBinaryString() deprecated? From W3C
The use of readAsArrayBuffer() is preferred over readAsBinaryString(), which is provided for backwards compatibility.
readAsBinaryString returns a completely different thing than the other method, so how could one be the replacement for the other?
In my specific case I have a Blob that I need to convert to base64, there are many ways, but most of them not memory efficient. As of my tests calling window.btoa() from readAsBinaryString' result works best. If I cannot use this anymore (or for now lets say "should"), then I must convert the array to a string using iteration and strings concatenation which is not memory efficient at all!
So after researching for days I don't really find an alternative to readAsBinaryString, that's why the question, or do you see an alternative that also works with 100MB blobs?

The history is that readAsBinaryString was present in an early specification of the FileReader API, before the ArrayBuffer interface exist.
When the ArrayBuffer interface appeared, readAsBinaryString has been deprecated because all its use cases could be done in a better way using that new interface.
Indeed, readAsBinaryString only converts the binary data into a DOMString (UTF-16). There is not much you can do from it afterward. Also, storing it as an UTF-16 string implies that it takes far more space in memory than the original data size. Add to this that strings are immutable, I guess you can see how inefficient it is to work from this.
And finally, if you really need to have this string, you can actually do the same from an ArrayBuffer, you just need to call String.fromCharCode over an Uint8 view of this ArrayBuffer.
// generate some binary data
document.createElement('canvas').toBlob(blob => {
const bin_reader = new FileReader();
const arr_reader = new FileReader();
let done = 0;
bin_reader.onload = arr_reader.onload = e => {
if(++done===2) {
const arr_as_bin = [...new Uint8Array(arr_reader.result)]
.map(v => String.fromCharCode(v)).join('');
console.log('same results: ', arr_as_bin === bin_reader.result);
console.log(arr_as_bin);
}
}
bin_reader.readAsBinaryString(blob);
arr_reader.readAsArrayBuffer(blob);
});
Now, this method, while still very useless, has been re-added to the specs, because some websites did start using it.
And to help OP a bit more, since what they were trying to do was actually to get a base64 version of their Blob, then don't even use readAsArrayBuffer(), readAsDataURL() is what you need:
const blob = new Blob(['hello']);
const reader = new FileReader();
reader.onload = e => {
const dataURL = reader.result;
const base64 = dataURL.slice(dataURL.indexOf(',')+1);
console.log(base64);
};
reader.readAsDataURL(blob);

Related

What to use instead of Buffer(bitmap)

I'm using the following method to convert files into base64 encoding and it's been working fine for a long time, but I see now that Buffer is depricated.
// function to encode file data to base64 encoded string
function base64_encode(file) {
var bitmap = fs.readFileSync(file);
// convert binary data to base64 encoded string
return new Buffer(bitmap).toString("base64");
}
let base64String = base64_encode("Document.png");
Can someone please help me modify this to work with the new suggested method as I'm not sure how to modify it myself?
Thank you so much in advance.
It is not Buffer that is deprecated but its constructor, so instead of new Buffer() you use e.g. Buffer.from.
However fs.readFileSync already returns a Buffer if no encoding is specified, so there is not really a need to pass that to another buffer. Instead you can do return fs.readFileSync(file).toString("base64")
Using the Sync part of the API is most of the time something would like to avoid and if possible switch over to the promise-based API.

How to convert Javascript Object to ArrayBuffer?

I retrieve an encoded string (using TextEncoder into UTF-8, which was stringified before sending to the server) from the server using AJAX. I parse it upon retrieval and get an Object. I need to convert this Object to a decoded string. TextDecoder seems to have decode method, but it expects ArrayBuffer or ArrayBufferView, not Object. That method gives TypeError if I use my Object as-is:
var myStr = "This is a string, possibly with utf-8 or utf-16 chars.";
console.log("Original: " + myStr);
var encoded = new TextEncoder("UTF-16").encode(myStr);
console.log("Encoded: " + encoded);
var encStr = JSON.stringify(encoded);
console.log("Stringfied: " + encStr);
//---------- Send it to the server; store in db; retrieve it later ---------
var parsedObj = JSON.parse(encStr); // Returns an "Object"
console.log("Parsed: " + parsedObj);
// The following decode method expects ArrayBuffer or ArrayBufferView only
var decStr = new TextDecoder("UTF-16").decode(parsedObj); // TypeError
// Do something with the decoded string
This SO 6965107 has extensive discussion on converting strings/ArrayBuffers but none of those answers work for my situation. I also came across this article, which does not work if I have Object.
Some posts suggest to use "responseType: arraybuffer" which results in ArrayBuffer response from the server, but I cannot use it when retrieving this encoded string because there are many other items in the same result data which need different content-type.
I am kind of stuck and unable to find a solution after searching for a day on google and SO. I am open to any solution that lets me save "strings containing international characters" to the server and "retrieve them exactly as they were", except changing the content-type because these strings are bundled within JSON objects that carry audio, video, and files. Any help or suggestions are highly appreciated.

How convert a string to type Uint8Array in nodejs

How do I convert a string to a Uint8Array in node?
I don't normally develop in Javascript and this is driving me nuts. They offer a conversion Uint8Array.toString() but not the other way around. Does anyone know of an easy way for me to do this without creating my own parser?
I have seen some other answers to this, but they don't seem to address this specific class type
You can use Buffer.from(string[, encoding]). The Buffer class has implemented the Uint8Array interface in Node since v4.x. You can also optionally specify an encoding with which to do string processing in both directions, i.e. buffer.toString([encoding]).
Uint8Array.from(text.split('').map(letter => letter.charCodeAt(0)));
or (appears about 18% faster):
Uint8Array.from(Array.from(text).map(letter => letter.charCodeAt(0)));
Note: These only work for ASCII, so all the letters are modulo 256, so if you try with Russian text, it won't work.
Why not simply using:
const resEncodedMessage = new TextEncoder().encode(message)
This is how I do it when grabbing a string
var myString = "[4,12,43]"
var stringToArray = myString.replace('[', '').replace(']', '').split(',');
var stringToIntArray = Uint8Array.from(stringToArray );
console.log(stringToIntArray);

AS3 javascript callback issue in chrome

I'm making a callback from javascript to a method on my swf. It works in Firefox with no problems, but in chrome the second parameter that's passed is always received as null?
I've debugged my javascript and found that everything is working fine and the two values that are passed to the swf are correct at the point where the callback to the swf is made.
At first i thought this might be a cross domain issue, but have ruled that out as if it were the case then the method on swf would just not be called at all. The second value is a binary string representation of an image and the length of the string that is passed is 101601, so i'm wondering whether there's possibly a limitation on the amount of data that can be passed? The first parameter is a much smaller string representing the file type and this always gets received successfully.
Like i said, the strange thing is, it works perfectly fine in Firefox?
NOTE - so i've just tried it with a much smaller image (stupidly it hadn't occurred to me to test that until i wrote this), where the string length is only 133 and it still fails. So that rules that out.
I've also checked the AS3 docs and it doesn't appear to mention any such limitation.
The string is being produced using the FileReader class's readAsBinaryString() method. As far as i'm aware this outputs a UTF 16 string representation of the binary it receives. Although i think i'm right in thinking that this shouldn't be an issue, as it's still just a string and the encoding only really affects the decoding?
Javascript
var readFile = function(file)
{
var reader = new FileReader();
reader.onloadend = function( evt )
{
alert( reader.result.length );//this outputs the correct length
alert( reader.result ); //this outputs the binary encoded as a String
swf.addImage( file.type, reader.result );
}
reader.readAsBinaryString(file);
}
AS3
ExternalInterface.addCallback( "addImage", addImageHandler );
and
private function addImageHandler( type:String, file:String ):void
{
trace( "type: ", type );//this traces the type correctly
trace( "file: ", file );//this traces out null in chrome, but traces the binary string in firefox
}
So there appears to be an issue with passing the UTF-16 encoded string from javascript to flash, but only in chrome?
I'm not sure why this would be the case, but if i encode the UTF-16 string to base46, or convert it from UTF-16 to UTF-8 within the Javascript, before i pass it to the swf, then everything works as expected.
EDIT
On further testing, the solution turned out to be simpler than expected, it was just a case of calling encodeURI on the UTF-16 string and then calling decodeURI at the other end.
Javascript
var reader = new FileReader();
reader.onloadend = function( evt )
{
swf.addImage( file.type, encodeURI( reader.result ) );
}
reader.readAsBinaryString(file);
AS3
private function dropHandler( type:String, file:String ):void
{
file = decodeURI( file );
...
}
Of course without a code sample from you this might not help at all, but in case it could:
This is how I invoke a JS function from AS3 with say 2 parameters:
ExternalInterface.call("JavaScriptFunctionName", arg1_value, arg2_value);
Where this is the JS function:
function JavaScriptFunctionName(arg1, arg2) { ...

Returning a byte string to ExternalInterface.call throws an error

I am working on my open source project Downloadify, and up until now it simply handles returning Strings in response to ExternalInterface.call commands.
I am trying to put together a test case using JSZip and Downloadify together, the end result being that a Zip file is created dynamically in the browser, then saved to the disk using FileReference.save. However, this is my problem:
The JSZip library can return either a base64 encoded string of the Zip, or the raw byte string. The problem is, if I return that byte string in response to the ExternalInterface.call command, I get this error:
Error #1085: The element type "string" must be terminated by the matching end-tag "</string>"
ActionScript 3:
var theData:* = ExternalInterface.call('Downloadify.getTextForSave',queue_name);
Where queue_name is just a string used to identify the correct instance in JS.
JavaScript:
var zip = new JSZip();
zip.add("test.txt", "Hello world!\n");
var content = zip.generate(true);
return content;
If I instead return a normal string instead of the byte string, the call works correctly.I would like to avoid using base64 as I would have to include a base64 decoder in my swf which will increase its size.
Finally: I am not looking for a AS3 Zip generator. It is imperative to my project to have that part run in JavaScript
I am admittedly not a AS3 programmer by trade, so if you need any more detail please let me know.
When data is being returned from javascript calls it's being serialized into an XML string. So if the "raw string" returned by JSZip will include characters which make the XML non-valid, which is what I think is happening here, you'll get errors like that.
What you get as a return is actually:
<string>[your JSZip generated string]</string>
Imagine your return string includes a "<" char - this will make the xml invalid, and it's hard to tell what character codes will a raw byte stream translate too.
You can read more about the external API's XML format on LiveDocs
i think the problem is caused by the fact, that flash expects a utf8 String and you throw some binary stuff at it. i think for example 0x00FF will not turn out to be valid utf8 ...
you can try fiddling around with flash.system::System.setCodePage, but i wouldn't be too optimistic ...
i guess a base64 decoder is probably really the easiest ... i'd rather worry about speed than about file size though ... this rudimentary decoder method uses less than half a K:
public function decodeBase64(source:String):ByteArray {
var ret:ByteArray = new ByteArray();
var map:Object = new Object();
var i:int = 0;
for each (var char:String in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("")) map[char] = i++;
map["="] = 0;
source = source.split("\n").join("").split("\r").join("");//remove linebreaks
for (i = 0; i < source.length/4; i++) {
var buf:int = 0;
for each (char in source.substr(i * 4, 4).split("")) buf = (buf << 6) + map[char];
ret.writeByte(buf >>> 16);
ret.writeShort(buf);
}
return ret;
}
you could simply shorten function names and take a smaller image ... or use ColorTransform or ConvolutionFilter on one image instead of four ... or compile the image into the SWF for smaller overall size ... or reduce function name length ...
so unless you're planning on working with MBs of data, this is the way to go ...

Categories