Node.js throws "btoa is not defined" error - node.js

In my node.js application I did an npm install btoa-atob so that I could use the btoa() and atob() functions which are native in client-side javascript but for some reason weren't included in node. The new directory showed up in my node_modules folder, which itself is in root alongside app.js. Then I made sure to add btoa-atob as a dependency in my package.json file which is in root.
However, for some reason, it still will not work.
console.log(btoa("Hello World!"));
^ should output "SGVsbG8gV29ybGQh" to the console, but instead, I get the error:
btoa is not defined.
Did I not do the install properly? What did I overlook?

The 'btoa-atob' module does not export a programmatic interface, it only provides command line utilities.
If you need to convert to Base64 you could do so using Buffer:
console.log(Buffer.from('Hello World!').toString('base64'));
Reverse (assuming the content you're decoding is a utf8 string):
console.log(Buffer.from(b64Encoded, 'base64').toString());
Note: prior to Node v4, use new Buffer rather than Buffer.from.

The solutions posted here don't work in non-ascii characters (i.e. if you plan to exchange base64 between Node.js and a browser). In order to make it work you have to mark the input text as 'binary'.
Buffer.from('Hélló wórld!!', 'binary').toString('base64')
This gives you SOlsbPMgd/NybGQhIQ==. If you make atob('SOlsbPMgd/NybGQhIQ==') in a browser it will decode it in the right way. It will do it right also in Node.js via:
Buffer.from('SOlsbPMgd/NybGQhIQ==', 'base64').toString('binary')
If you don't do the "binary part", you will decode wrongly the special chars.
I got it from the implementation of the btoa npm package:

My team ran into this problem when using Node with React Native and PouchDB. Here is how we solved it...
NPM install buffer:
$ npm install --save buffer
Ensure Buffer, btoa, and atob are loaded as a globals:
global.Buffer = global.Buffer || require('buffer').Buffer;
if (typeof btoa === 'undefined') {
global.btoa = function (str) {
return new Buffer(str, 'binary').toString('base64');
};
}
if (typeof atob === 'undefined') {
global.atob = function (b64Encoded) {
return new Buffer(b64Encoded, 'base64').toString('binary');
};
}

export const universalBtoa = str => {
try {
return btoa(str);
} catch (err) {
return Buffer.from(str).toString('base64');
}
};
export const universalAtob = b64Encoded => {
try {
return atob(b64Encoded);
} catch (err) {
return Buffer.from(b64Encoded, 'base64').toString();
}
};

I found that although the shims from answers above worked, they did not match the behaviour of desktop browsers' implementations of btoa() and atob():
const btoa = function(str){ return Buffer.from(str).toString('base64'); }
// returns "4pyT", yet in desktop Chrome would throw an error.
btoa('✓');
// returns "fsO1w6bCvA==", yet in desktop Chrome would return "fvXmvA=="
btoa(String.fromCharCode.apply(null, new Uint8Array([0x7e, 0xf5, 0xe6, 0xbc])));
As it turns out, Buffer instances represent/interpret strings encoded in UTF-8 by default. By contrast, in desktop Chrome, you can't even input a string that contains characters outside of the latin1 range into btoa(), as it will throw an exception: Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.
Therefore, you need to explicitly set the encoding type to latin1 in order for your Node.js shim to match the encoding type of desktop Chrome:
const btoaLatin1 = function(str) { return Buffer.from(str, 'latin1').toString('base64'); }
const atobLatin1 = function(b64Encoded) {return Buffer.from(b64Encoded, 'base64').toString('latin1');}
const btoaUTF8 = function(str) { return Buffer.from(str, 'utf8').toString('base64'); }
const atobUTF8 = function(b64Encoded) {return Buffer.from(b64Encoded, 'base64').toString('utf8');}
btoaLatin1('✓'); // returns "Ew==" (would be preferable for it to throw error because this is undecodable)
atobLatin1(btoa('✓')); // returns "\u0019" (END OF MEDIUM)
btoaUTF8('✓'); // returns "4pyT"
atobUTF8(btoa('✓')); // returns "✓"
// returns "fvXmvA==", just like desktop Chrome
btoaLatin1(String.fromCharCode.apply(null, new Uint8Array([0x7e, 0xf5, 0xe6, 0xbc])));
// returns "fsO1w6bCvA=="
btoaUTF8(String.fromCharCode.apply(null, new Uint8Array([0x7e, 0xf5, 0xe6, 0xbc])));

I have a code shared between server and client and I needed an implementation of btoa inside it.
I tried doing something like:
const btoaImplementation = btoa || (str => Buffer.from(str).toString('base64'));
but the Server would crush with:
ReferenceError: btoa is not defined
while Buffer is not defined on the client.
I couldn't check window.btoa (it's a shared code, remember?)
So I ended up with this implementation:
const btoaImplementation = str => {
try {
return btoa(str);
} catch(err) {
return Buffer.from(str).toString('base64')
}
};

I was able to use btoa for binary data to base 64 string conversion using below npm package:
https://www.npmjs.com/package/btoa
As described in their documentation, I did below steps in node JS application:
Install => npm install --save btoa
Declare at top => const btoa = require('btoa');
Use => const b64 = btoa("stringToEncode");

Here's a concise universal solution for base64 encoding:
const nodeBtoa = (b) => Buffer.from(b).toString('base64');
export const base64encode = typeof btoa !== 'undefined' ? btoa : nodeBtoa;

Anybody looking to decode:
let decoded = Buffer.from(<encoded string>, 'base64').toString()
Because I came here looking for decoding, ended up figuring it out from an answer here.

Same problem with the 'script' plugin in the Atom editor, which is an old version of node, not having btoa(), nor atob(), nor does it support the Buffer datatype. Following code does the trick:
var Base64 = new function() {
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
this.encode = function(input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = Base64._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
}
return output;
}
this.decode = function(input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = Base64._utf8_decode(output);
return output;
}
this._utf8_encode = function(string) {
string = string.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
this._utf8_decode = function(utftext) {
var string = "";
var i = 0;
var c = 0,
c1 = 0,
c2 = 0,
c3 = 0;
while (i < utftext.length) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if ((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i + 1);
c3 = utftext.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}()
var btoa = Base64.encode;
var atob = Base64.decode;
console.log("btoa('A') = " + btoa('A'));
console.log("btoa('QQ==') = " + atob('QQ=='));
console.log("btoa('B') = " + btoa('B'));
console.log("btoa('Qg==') = " + atob('Qg=='));

If you end up here, looking for atob is not defined solution (like me). Try upgrade your nodejs version - it helps me

I understand this is a discussion point for a node application, but in the interest of universal JavaScript applications running on a node server, which is how I arrived at this post, I have been researching this for a universal / isomorphic react app I have been building, and the package abab worked for me. In fact it was the only solution I could find that worked, rather than using the Buffer method also mentioned (I had typescript issues).
(This package is used by jsdom, which in turn is used by the window package.)
Getting back to my point; based on this, perhaps if this functionality is already written as an npm package like the one you mentioned, and has it's own algorithm based on W3 spec, you could install and use the abab package rather than writing you own function that may or may not be accurate based on encoding.
---EDIT---
I started having weird issues today with encoding (not sure why it's started happening now) with package abab. It seems to encode correctly most of the time, but sometimes on front end it encodes incorrectly. Spent a long time trying to debug, but switched to package base-64 as recommended, and it worked straight away. Definitely seemed to be down to the base64 algorithm of abab.

Related

How to convert string and integer to binary in nodejs?

I have the following problems. I have an integer and a string. Both of them need to be converted into binary format. For the integer I found a solution that, as far as I can tell, works. The string on the other hand, I don't have a solid understanding of it.
String(16), as far as I understand, means something like Array<UInt8> and has a fixed length of 16. Am I correct? If so, is there a better way to converting them by hand built in in NodeJS?
const myNumber = 2
const myString = 'MyString'
const myNumberInBinary = toUInt16(myNumber) // eg. 0000000000000101
const myStringinBinary = toString16(myString) // I honestly don't know
function toUInt16(number) {
let binaryString = Number(number).toString(2)
while (binaryString.length < 16) {
binaryString = '0' + binaryString
}
return binaryString
}
// TODO: implement
function toString16(string) {
...
return binaryString
}
best regards
EDIT:
Thanks for all the comments and the answer. They helped me understand this process better. The solution I ended up with is this one:
const bitString = "00000101"
const buffer = new Buffer.alloc(bitString.length / 8)
for (let i = 0; i < bitString.length; i++) {
const value = bitString.substring(i * 8, (i * 8) + 8)
buffer[i] = Number(value)
}
fs.writeFileSync('my_file', buffer, 'binary')
Thanks again!
You should loop through the string and do this on each character:
let result = ""
for (let i = 0; i < myString.length; i++) {
result += myString[i].charCodeAt(0).toString(2) + " ";
}

BodyPix - running toMask() and toColoredPartMask() in node.js throws an error: ImageData is not defined

I am trying to get segmented person parts colored mask in TensorFlowJS bodypix model. Below code works fine before toColoredPartMask or toMask which throws an error "ImageData is not defined".
const tfjsnode = require('#tensorflow/tfjs-node')
const bodyPix = require('#tensorflow-models/body-pix');
const fs = require('fs');
setTimeout(async () => {
maskImageWithBodyPix().then(response => {
console.log(response)
}).catch(e => {
console.log("Error => " + e)
})
}, 1000)
async function maskImageWithBodyPix(image = readImage("./person.jpeg")) {
console.log("loadModel ...");
if (image == null)
return Promise.resolve("Image Not Found...")
const resNet = {
architecture: 'ResNet50',
outputStride: 16,
quantBytes: 4
};
let bodyModel = await bodyPix.load(resNet)
console.log("segmentPersonParts ...");
let segmentedPersonParts = await bodyModel.segmentPersonParts(image, {
flipHorizontal: false,
internalResolution: 'full',
segmentationThreshold: 0.5,
})
console.log(`ImageHeight: ${segmentedPersonParts.height} | ImageWidth: ${segmentedPersonParts.width}`)
console.log("toMaskImageData ...")
const maskedImageData = bodyPix.toColoredPartMask(segmentedPersonParts, false);
console.log(`maskedImageData = ${maskedImageData}`)
return Promise.resolve(true)
}
const readImage = path => {
console.log(`readImage ...`)
if (!fs.existsSync(path))
return null
const imageBuffer = fs.readFileSync(path);
const tfimage = tfjsnode.node.decodeImage(imageBuffer);
return tfimage;
}
So this is a huge hack, but I got it working.
The problem lies in node_modules\#tensorflow-models\body-pix\dist\output_rendering_util.js.
If we look at this function inside:
function toColoredPartMask(partSegmentation, partColors) {
if (partColors === void 0) { partColors = RAINBOW_PART_COLORS; }
if (Array.isArray(partSegmentation) && partSegmentation.length === 0) {
return null;
}
var multiPersonPartSegmentation;
if (!Array.isArray(partSegmentation)) {
multiPersonPartSegmentation = [partSegmentation];
}
else {
multiPersonPartSegmentation = partSegmentation;
}
var _a = multiPersonPartSegmentation[0], width = _a.width, height = _a.height;
var bytes = new Uint8ClampedArray(width * height * 4);
for (var i = 0; i < height * width; ++i) {
// invert mask. Invert the segmentation mask.
var j = i * 4;
bytes[j + 0] = 255;
bytes[j + 1] = 255;
bytes[j + 2] = 255;
bytes[j + 3] = 255;
for (var k = 0; k < multiPersonPartSegmentation.length; k++) {
var partId = multiPersonPartSegmentation[k].data[i];
if (partId !== -1) {
var color = partColors[partId];
if (!color) {
throw new Error("No color could be found for part id " + partId);
}
bytes[j + 0] = color[0];
bytes[j + 1] = color[1];
bytes[j + 2] = color[2];
bytes[j + 3] = 255;
}
}
}
return new ImageData(bytes, width, height);
}
It seemingly returns a new ImageData object. However, if we look at what ImageData actually is, we get this:
And if we dig deeper, we get to some node.js internal typescript files and see this:
declare var ImageData: {
prototype: ImageData;
new(sw: number, sh: number, settings?: ImageDataSettings): ImageData;
new(data: Uint8ClampedArray, sw: number, sh?: number, settings?: ImageDataSettings): ImageData;
};
I thought that this won't work for us, so we need to overwrite that reference somehow.
What I did was install this package here and added const ImageData = require("#canvas/image-data"); line to the very top of the node_modules\#tensorflow-models\body-pix\dist\output_rendering_util.js file like this:
"use strict";
/**
* #license
* Copyright 2019 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/
const ImageData = require("#canvas/image-data");
Object.defineProperty(exports, "__esModule", { value: true });
...
Now when we look at the ImageData again, we get this:
Just by doing that and also removing the false parameter from toColoredPartMask-call, so it's called like this:
const maskedImageData = bodyPix.toColoredPartMask(segmentedPersonParts);
We get this result:
If we were to JSON.stringify(maskedImageData), we'd get a billion lines of some numbers, so I'm not going to paste that here. Anyway, I think it works now.
I think the problem lies in the node.js implementation of ImageData. We're using some kind of weird browser implementation of it. The package I installed implements an outside of browser version of ImageData and we force bodypix to use that instead with the require.
Here is my input image:
And here is the output:
DO NOTE THAT YOU SHOULD BASICALLY NEVER EDIT ANYTHING INSIDE node_modules, BUT IF THIS IS JUST A PROJECT FOR FUN, I DON'T SEE WHY NOT PLAY AROUND A LITTLE
P.S I think the first line in toColoredPartMask that checks partColors is a bit funky, so I replaced it with if(arguments.length === 1 || !partColors) { partColors = RAINBOW_PART_COLORS; } in my code.

How to manipulate a string representing a raw number (e.g. 130000.1293) into a formatted string (e.g. 130,000.13)?

In apps script I want to obtain formatted 'number' strings. The input is an unformatted number. With an earlier answer posted by #slandau, I thought I had found a solution by modifying his code (see code snippet). It works in codepen, but not when I am using apps script.
1. Does anyone know what went wrong here?
2. I noticed this code works except when entering a number ending in .0, in that case the return value is also .0 but should be .00. I would like some help fixing that too.
Thanks!
I have tried to look for type coercion issues, but wasn't able to get it down. I am fairly new to coding.
function commaFormatted(amount)
{
var delimiter = ","; // replace comma if desired
var a = amount.split('.', 2);
var preD = a[1]/(Math.pow(10,a[1].length-2));
var d = Math.round(preD);
var i = parseInt(a[0]);
if(isNaN(i)) { return ''; }
var minus = '';
if(i < 0) { minus = '-'; }
i = Math.abs(i);
var n = new String(i);
var a = [];
while(n.length > 3)
{
var nn = n.substr(n.length-3);
a.unshift(nn);
n = n.substr(0,n.length-3);
}
if(n.length > 0) { a.unshift(n); }
n = a.join(delimiter);
if(d.length < 1) { amount = n; }
else { amount = n + '.' + d; }
amount = minus + amount;
return amount;
}
console.log(commaFormatted('100000.3532'))
The expected result would be 100,000.35.
I am getting this in the IDE of codepen, but in GAS IDE is stops at the .split() method => not a function. When converting var a to a string = I am not getting ["100000", "3532"] when logging var a. Instead I am getting 100000 and was expecting 3532.
Based on this answer, your function can be rewritten to
function commaFormatted(amount)
{
var inputAmount;
if (typeof(amount) == 'string') {
inputAmount = amount;
} else if (typeof(amount) == 'float') {
inputAmount = amount.toString();
}
//--- we expect the input amount is a String
// to make is easier, round the decimal part first
var roundedAmount = parseFloat(amount).toFixed(2);
//--- now split it and add the commas
var parts = roundedAmount.split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
}
console.log(commaFormatted(100000.3532));
console.log(commaFormatted('1234567.3532'));

Buffer.swap16 is not a function

I've tested some buffer swap function in NodeJS, but in my Meteor project it doesn't work.
Can somebody tell me the reason?
Nodejs:
var CRC = new Buffer([0x20,0x40]);
console.log(CRC.swap16()); // OUTPUT: <Buffer 40 20>
Meteor.js
var CRC = new Buffer([0x20,0x40]);
console.log(CRC.swap16()); // OUTPUT: TypeError: CRC.swap16 is not a function
Can somebody help me please. In know it's maybe a version problem. but i can't solve it at the moment.
Is there also a way to get this thing running with meteor?
Buffer.swap16() method has been added in 5.10.0, whereas Meteor uses NodeJS version 4.
You can copy this method implementation from the NodeJS source, it is quite simple (with small modifications):
function swap(b, n, m) {
const i = b[n];
b[n] = b[m];
b[m] = i;
}
Buffer.prototype.swap16 = function swap16() {
const len = this.length;
if (len % 2 !== 0) {
throw new RangeError('ERR_INVALID_BUFFER_SIZE', '16-bits');
}
for (var i = 0; i < len; i += 2) {
swap(this, i, i + 1);
}
return this;
};

Node is not launching

I'm desperately trying to make node.js work again on Ubuntu 12.04 LTS.
I installed it before 2-3 weeks ago and everything went fine, I used it daily for that period of time.
But today, suddenly it just wouldn't work anymore. The way it bugs is really strange :
node -v works and returns v0.8.2
the node command works too, I can access the console and do a console.log
but when I use node with a file like this node server.js, Ubuntu just goes to a new line :
kollektiv#kollektiv-PC:~/node-projects$ node server.js
kollektiv#kollektiv-PC:~/node-projects$
I already reinstalled Ubuntu this evening but I get the same result.
I also did multiple apt-get upgrade and apt-get update in case some node.js dependencies would be out of date.
The way I installed node.js is by compiling the source following this tutorial : --> Compiling Node.js from source on Ubuntu 10.24 - shapeshed
I even did a chmod 777 server.js on the server file just to be sure but that didn't change anything either.
Thank you a lot in advance for your help !
EDIT : Content of server.js
var net = require('net'),
server = net.createServer();
var crypto = require('crypto'),
shasum = crypto.createHash('sha256');
var alpha = [],
i = 0,
cle = '';
while(i < 256) {
alpha.push(String.fromCharCode(i));
i++;
}
// CRYPTAGE -- START --
function cryptProcess(cle, txt) {
var k = txt.length,
j = k / cle.length,
cledeBase = cle,
txtc = '',
i = 1;
while(i < j) {
cle = cle + cledeBase;
i++;
}
function crypt(cleu, letr) {
//if(alpha.indexOf(letr) == -1) return "§";
var biIndex = alpha.indexOf(letr) + alpha.indexOf(cleu), x;
sumIndex = biIndex - alpha.length;
x = sumIndex >= 0 ? alpha[sumIndex] : alpha[biIndex];
return x;
}
while(k--) {
txtc = crypt(cle[k], txt[k]) + txtc;
}
return txtc;
}
function decryptProcess(cle, txtc) {
var k = txtc.length,
j = k / cle.length,
cledeBase = cle,
txt = '',
i = 1;
while(i < j) {
cle = cle + cledeBase;
i++;
}
txt = '';
function decrypt(cleu, letc) {
//if(alpha.indexOf(letc) == -1) return "§";
var biIndex = letc - alpha.indexOf(cleu), x;
x = biIndex >= 0 ? alpha[biIndex] : alpha[biIndex + alphabet.length];
return x;
}
while(k--) {
txt = decrypt(cle[k], txtc[k]) + txt;
}
return txt;
}
// CRYPTAGE -- END --
server.on('connection', function(client) {
var connecOne = 0;
function talk(data) {
var msg = data.toString('utf8');
var msgEnc = cryptProcess(cle, msg);
client.write(msgEnc);
console.log(msg + '\nsend as\n' + msgEnc);
}
client.once('data', function(data) {
function triHandShake() {
}
});
client.on('data', function(data) {
var msg = data.toString('utf8');
if(connecOne === 0) {
connectionOne(msg);
connecOne++;
}
else if(connecOne === 1) {
// Check for paragraph symbol
//authentification with cookie as cle
}
var msgDec = decryptProcess(cle, msg);
console.log(msgDec + '\nreiceved as\n' + msgDec);
});
client.on('end', function() {
connecOne = 0;
});
});
You need to call server.listen to listen for connections and start the process as expected.
server.listen(8124, function() { //'listening' listener
console.log('server bound');
});

Resources