reading a binary file in javascript using browserify - javascript

I am trying to use browserify to access a local binary file (that is, the binary file is in the same directory as the javascript file, which is in the user's computer).
I haven't succeeded. Here's what I tried and what I know:
~) I know fs won't work...
0) I tried using the require('html') but it says 'ajax not supported in this browser' [I am using chromium... but I'd assume it's roughly the same thing as chrome].
1) I tried using 'browser-request'. This reads the binary file... as a string.
It is based on 'request' so I should be able to configure the options, including encoding: null, which would solve all my problems but...looking at the source code, you'll see that no support for the encoding option is present. Not even a warning.
2) I used xmlhttprequest, which required the 'html' module... so again, I get the same error as in 0) Strangely enough, 'browser-request' uses this module and it works... and I have absolutely no idea why.
3) At this point, I looked into html5 file system support. It would work but I don't want the user to specify a file... seeing as I really ONLY want to get the buffer to memory. Is there any other way to access the file? Perhaps using --allow-file-access when starting chromium?
4) If all else fails, I just want a way to get the Buffer into my code. I guess I could just use node on shell and copy paste the result of reading the file into memory...
Is there any hope at all?

Here's what somewhat works:
function toArrayBuffer(buffer) {
var ab = new ArrayBuffer(buffer.length);
var view = new Uint8Array(ab);
for (var i = 0; i < buffer.length; ++i) {
view[i] = buffer[i];
}
return ab;
}
// node: readFileSync + toArrayBuffer
// browser: ajax http request
function readFile(filename, doneCb) {
var isNode =
typeof global !== "undefined" &&
global.toString() == '[object global]';
if (isNode) {
var fs = require('fs');
var buffer = fs.readFileSync(filename);
buffer = toArrayBuffer(buffer);
doneCb(buffer);
} else {
var http = require('http');
var buf;
var req = http.get({ path : '/'+ filename }, function (res) {
res.on('data', function (chunk) {
buf = chunk;
});
res.on('end', function () {
doneCb(buf);
});
});
req.xhr.responseType = 'arraybuffer';
}
}
It requires a server and I'm strugging with on how to make it work in testling.
Another approach I can think of is to use brfs with base64 encoding:
var base64 = fs.readFileSync('file.bin', enc='base64');
var buf = new Buffer(base64, 'base64');
var ab = toArrayBuffer(buf);
It is simpler, but it is not dynamic and cannot be refactored to self-contained function.

If it's not dynamic use brfs transform.

Related

MD5 Checksum differes from browser to Node js API

I am trying to perform a checksum on a file with javascript. I'm using a FileReader and using CryptoJS with the .MD5 method as well as the CryptoJS.enc.Hex encoding.
The checksums differ from the front end (above) and on the back end, where I am getting it from ExpressFIleUpload and also generated my own with the crypto module via crypto.createHash('md5') and getting a digest via hash.digest('hex'); and out of those two, (my own and ExpressFileUpload) they too, differ..
what is going on..?
let img = document.createElement('img');
img.file = data;
let reader = new FileReader();
reader.onload = (function (someelement) {
return function (e) {
let md5 = CryptoJS.MD5(e.target.result);
let str = md5.toString(CryptoJS.enc.Hex);
console.log('str', str); // will give one random md5
};
})(img);
reader.readAsBinaryString(data);
then on the server using https://www.npmjs.com/package/express-fileupload
export async function(req, res, next) {
console.log(req.files.file.md5) // some other md5
const hash = crypto.createHash('md5');
let buff = Buffer.from(req.files.file.data, "base64").toString('utf-8');
// edit, this actually DOES come to the same if I remove
// .toString('utf-8')
// as the req.files.file.md5
hash.update(buff);
let str = hash.digest("hex");
console.log('other hash', str); // and some third completely different md5
}
can someone please explain what I am doing wrong?
Did you encode the e.target.result to UTF-8 in your front-end? I had kind of like the same problem, but then I realized, that I'd used the wrong encoding.
Try to encode your Plain String into utf-8 and hash it after that.

Blazor Server-Side JS Invoking

Hi I'm trying to move the bytes of my video which is in c# to javascript to make the bytes into URL.createObjectURL on Blazor server-side
I moved the bytes using Js Invoke
.cs
if (!string.IsNullOrEmpty(item.PathFile))
{
//Byte Video
byte[] result = GetFile(item.PathFile);
if (result != null)
{
var url = await Js.InvokeAsync<string>("videoUrl", result);
data.ImageString = url;
}
}
.js
function videoUrl(value) {
var byteCharacters = atob(value);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
//Byte Array -> Blob
var file = new Blob([byteArray], { type: 'data:video/mp4;base64' });
//Blob -> Object URL
var fileURL = URL.createObjectURL(file);
return fileURL;
}
My problem is, I tried this script for a video with a size of 3 Mb it runs fine, but when I try for a 133Mb video I get an error:
Error: System.ArgumentException: The JSON value of length 139569235 is too large and not supported.
I've tried to fix it, but it still fails, it makes me a little frustrated
So is there a solution for my error ? or what should I do?
I thank you for any suggestions or feedback
So, reading on the AspNetDocs github, there is a startup option that can change the max message size, but I think it only applies to calls from JS to .Net (https://github.com/dotnet/AspNetCore.Docs/issues/21208). Worth a check though.
services.AddServerSideBlazor()
.AddHubOptions(options => options.MaximumReceiveMessageSize = 32000);
Personally though, I would do as Mister Magoo said in the comment and either use an API or chunk the data and reassemble at the other end.

nwjs-nodejs- encrypt and decrypt img file (jpg) and use the decrypted data to an img element

I developed a desktop application with nwjs (nodejs / html / css ), now i want to put the app for the production so i need to prevent stealing my assets (my images are very valuables), nwjs provide a tool to compile (encrypt) the js files but not the asset so i thought about encrypting my assets with a js then encrypt the js with nwjs tool, i am not very familiare with node modules and dealing with files in js so i struggled with this task !
This code is what i tried to do but i did not reach my goal ?
encrypt
let crypto;
try {
crypto = require('crypto');
} catch (err) {
console.log('crypto support is disabled!');
}
var algorithm = 'aes-256-ctr',
password = 'secret';
var fs = require('fs');
var r;
// encrypt content
var encrypt = crypto.createCipher(algorithm, password);
// decrypt content
var decrypt = crypto.createDecipher(algorithm, password);
// write file
var w;
var path = require('path');
var dirPath = './Files/'; //directory path
var fileType = '.' + 'jpg'; //file extension
var files = [];
fs2.readdir(dirPath, function (err, list) {
if (err) throw err;
for (var i = 0; i < list.length; i++) {
if (path.extname(list[i]) === fileType) {
r = fs.createReadStream('Files/' + list[i]);
w = fs.createWriteStream('encFiles/' + list[i].replace('.jpg', ''));
console.log(list[i]); //print the file
// start pipe
r.pipe(encrypt).pipe(w);
}
}
});
decrypt
'use strict';
var crypt = require('crypto'),
algorithm = 'aes-256-ctr',
password = 'secret';
var fs = require('fs');
var zlib = require('zlib');
var toArray = require('stream-to-array');
// input file
var r = fs.createReadStream('./encFiles/an example file');
// decrypt content
var decrypt = crypt.createDecipher(algorithm, password);
//b64 module so i could put the base64 data to img html element
const B64 = require('b64');
const encoder = new B64.Encoder();
// start pipe
var stream = r.pipe(decrypt);
var d = stream.pipe(encoder);
d.pipe(process.stdout);
var data;
toArray(stream, function(err, arr) {
console.log(err,arr);
data = Buffer.concat(arr);
console.log(data);
});
console.log(data);
thank you for giving me comments on the code or other IDEAS
So the solution was so simple, I used the nw-js code protection feature to protected the script in which I decrypt the assets (images in my case) (this script contains the key of decryption), so you could implement the encryption/decryption with any method you want and protect the decryption script which is going to be shipped with you product (in my case the desktop app).
Since you are building a desktop app, you may want to look at cryptojs for this. I would still strongly suggest that you watermark images and hide them when your application looses focus. Even with that, screenshots can be taken without leaving your application.

Create image from ArrayBuffer in Nodejs

I'm trying to create an image file from chunks of ArrayBuffers.
all= fs.createWriteStream("out."+imgtype);
for(i=0; i<end; i++){
all.write(picarray[i]);
}
all.end();
where picarray contains ArrayBuffer chunks. However, I get the error TypeError: Invalid non-string/buffer chunk.
How can I convert ArrayBuffer chunks into an image?
Have you tried first converting it into a node.js. Buffer? (this is the native node.js Buffer interface, whereas ArrayBuffer is the interface for the browser and not completely supported for node.js write operations).
Something along the line of this should help:
all= fs.createWriteStream("out."+imgtype);
for(i=0; i<end; i++){
var buffer = new Buffer( new Uint8Array(picarray[i]) );
all.write(buffer);
}
all.end();
after spending some time i got this, it worked for me perfectly.
as mentioned by #Nick you will have to convert buffer array you recieved from browser in to nodejs Buffer.
var readWriteFile = function (req) {
var fs = require('fs');
var data = new Buffer(req);
fs.writeFile('fileName.png', data, 'binary', function (err) {
if (err) {
console.log("There was an error writing the image")
}
else {
console.log("The sheel file was written")
}
});
});
};
Array Buffer is browser supported which will be unsupportable for writing file, we need to convert to Buffer native api of NodeJs runtime engine.
This few lines of code will create image.
const fs = require('fs');
let data = arrayBuffer // you image stored on arrayBuffer variable;
data = Buffer.from(data);
fs.writeFile(`Assets/test.png`, data, err => { // Assets is a folder present in your root directory
if (err) {
console.log(err);
} else {
console.log('File created successfully!');
}
});

How to change emscripten browser input method from window.prompt to something more sensible?

I have a C++ function which once called consumes input from stdin. Exporting this function to javascript using emscripten causes calls to window.prompt.
Interacting with browser prompt is really tedious task. First of all you can paste only one line at time. Secondly the only way to indicate EOF is by pressing 'cancel'. Last but not least the only way (in case of my function) to make it stop asking user for input by window.prompt is by checking the checkbox preventing more prompts to pop up.
For me the best input method would be reading some blob. I know I can hack library.js but I see some problems:
Reading blob is asynchronous.
To read a blob, first you have to open a file user has to select first.
I don't really know how to prevent my function from reading this blob forever - there is no checkbox like with window.prompt and I'm not sure if spotting EOF will stop it if it didn't in window.prompt case (only checking a checkbox helped).
The best solution would be some kind of callback but I would like to see sime hints from more experienced users.
A way would be to use the Emscripten Filesystem API, for example by calling FS.init in the Module preRun function, passing a custom function as the standard input.
var Module = {
preRun: function() {
function stdin() {
// Return ASCII code of character, or null if no input
}
var stdout = null; // Keep as default
var stderr = null; // Keep as default
FS.init(stdin, stdout, stderr);
}
};
The function is quite low-level: is must deal with one character at a time. To read some data from a blob, you could do something like:
var data = new Int8Array([1,2,3,4,5]);
var blob = new Blob([array], {type: 'application/octet-binary'});
var reader = new FileReader();
var result;
reader.addEventListener("loadend", function() {
result = new Int8Array(reader.result);
});
var i = 0;
var Module = {
preRun: function() {
function stdin() {
if (if < result.byteLength {
var code = result[i];
++i;
return code;
} else {
return null;
}
}
var stdout = null; // Keep as default
var stderr = null; // Keep as default
FS.init(stdin, stdout, stderr);
}
};
Note (as you have hinted), due to the asynchronous nature of the reader, there could be a race condition: the reader must have loaded before you can expect the data at the standard input. You might need to implement some mechanism to avoid this in a real case. Depending on your exact requirements, you could make it so the Emscripten program doesn't actually call main() until you have the data:
var fileRead = false;
var initialised = false;
var result;
var array = new Int8Array([1,2,3,4,5]);
var blob = new Blob([array], {type: 'application/octet-binary'});
var reader = new FileReader();
reader.addEventListener("loadend", function() {
result = new Int8Array(reader.result);
fileRead = true;
runIfCan();
});
reader.readAsArrayBuffer(blob);
var i = 0;
var Module = {
preRun: function() {
function stdin() {
if (i < result.byteLength)
{
var code = result[i];
++i;
return code;
} else{
return null;
}
}
var stdout = null;
var stderr = null;
FS.init(stdin, stdout, stderr);
initialised = true;
runIfCan();
},
noInitialRun: true
};
function runIfCan() {
if (fileRead && initialised) {
// Module.run() doesn't seem to work here
Module.callMain();
}
}
Note: this is a version of my answer at Providing stdin to an emscripten HTML program? , but with focus on the standard input, and adding parts about passing data from a Blob.
From what I understand you could try the following:
Implement selecting a file in Javascript and access it via Javascript Blob interface.
Allocate some memory in Emscripten
var buf = Module._malloc( blob.size );
Write the content of your Blob into the returned memory location from Javascript.
Module.HEAPU8.set( new Uint8Array(blob), buf );
Pass that memory location to a second Emscripten compiled function, which then processes the file content and
Deallocate allocated memory.
Module._free( buf );
Best to read the wiki first.

Categories

Resources