How to checksum the file to be uploaded with javascript? - javascript

I want to checksum the files at browser side before uploading, then checksum and compare at server side to make sure the consistent. But how can I get the pure binary data of the file and checksum it? I tried the way below, but doesn't work:
let fileSelect = document.getElementById('file')
let files = fileSelect.files
let file = files[0]
var r = new FileReader();
r.onload = function(){ console.log(r.result); };
r.readAsArrayBuffer(file);
var file_sha1 = sha1(r.result)

The library you are using seems to support string input only.
Find a library support binary input. Eg. js-sha1. And you should hash it in the callback.
var reader = new FileReader();
reader.onload = function (event) {
var file_sha1 = sha1(event.target.result)
};
reader.readAsArrayBuffer(file);

Related

Proper way to read a file using FileReader() to generate an md5 hash string from image files?

I'm currently doing this (see snippet below) to get an md5 hash string for the image files I'm uploading (I'm using the hash as fileNames):
NOTE: I'm using the md5 package to generate the hash (it's loaded into the snippet).
There are 4 available methods on FileReader() to read the files. They all seem to produce good results.
readAsText(file)
readAsBinaryString(file);
readAsArrayBuffer(file);
readAsDataURL(file);
Which is should I be using in this case and why? Can you also explain the difference between them?
function onFileSelect(e) {
const file = e.target.files[0];
const reader1 = new FileReader();
const reader2 = new FileReader();
const reader3 = new FileReader();
const reader4 = new FileReader();
reader1.onload = (event) => {
const fileContent = event.target.result;
console.log('Hash from "readAsText()": ');
console.log(md5(fileContent));
}
reader2.onload = (event) => {
const fileContent = event.target.result;
console.log('Hash from "readAsBinaryString()": ');
console.log(md5(fileContent));
}
reader3.onload = (event) => {
const fileContent = event.target.result;
console.log('Hash from "readAsArrayBuffer()": ');
console.log(md5(fileContent));
}
reader4.onload = (event) => {
const fileContent = event.target.result;
console.log('Hash from "readAsDataURL()": ');
console.log(md5(fileContent));
}
reader1.readAsText(file);
reader2.readAsBinaryString(file);
reader3.readAsArrayBuffer(file);
reader4.readAsDataURL(file);
}
.myDiv {
margin-bottom: 10px;
}
<script src="https://cdn.jsdelivr.net/npm/js-md5#0.7.3/src/md5.min.js"></script>
<div class="myDiv">Pick an image file to see the 4 hash results on console.log()</div>
<input type='file' onChange="onFileSelect(event)" accept='.jpg,.jpeg,.png,.gif' />
Use readAsArrayBuffer.
readAsBinaryString() and readAsDataURL() will make your computer do a lot more work than what needs to be done:
read the blob as binary stream
convert to UTF-16 / base64 String (remember strings are not mutable in js, any operation you do on it will actually create a copy in memory)
[ pass to your lib ]
convert to binary string
process the data
Also, it seems your library doesn't handle data URLs and fails on UTF-16 strings.
readAsText() by default will try to interpret you binary data as an UTF-8 text sequence, which is pretty bad for binary data like raster image:
// generate some binary data
document.createElement('canvas').toBlob(blob => {
const utf8_reader = new FileReader();
const bin_reader = new FileReader();
let done = 0;
utf8_reader.onload = bin_reader.onload = e => {
if(++done===2) {
console.log('same results: ', bin_reader.result === utf8_reader.result);
console.log("utf8\n", utf8_reader.result);
console.log("utf16\n", bin_reader.result);
}
}
utf8_reader.readAsText(blob);
bin_reader.readAsBinaryString(blob);
});
readAsArrayBuffer on the other hand will just allocate the binary data as is in memory. Simple I/O, no processing.
To manipulate this data, we can use TypedArrays views over this binary data, which being only views, won't create any overhead either.
And if you look at the library you are using, they will anyway pass your input to such an Uint8Array to further process it. However beware they apparently need you to pass an Uint8Array view of this ArrayBuffer instead of the nude ArrayBuffer directly.

Converting readAsBinaryString to Data:URI

I have a binaryString variable as readAsBinaryString. How can I convert it to data:uri?
var reader = new FileReader();
reader.onloadend = function () {
var binaryString = reader.result;
console.log(binaryString);
console.log(window.btoa(binaryString));//does not contain mime-type at the beginning
}
var file=document.getElementById("fileToUpload").files[0];
reader.readAsBinaryString(file);
I need to operate over directly binaryString. I am sending the binaryString to another Javascript application. So in that context I only have binaryString value. The code above is just an example. Please do not recommend URL.createObjectURL or FileRader.readAsDataURL.
I need to operate over binaryString directly and convert it data:uri.

Convert binary file to JavaScript string and then to Uint8Array

I'm trying to create a web application that can be used via a file:// URI. This means that I can't use AJAX to load binary files (without turning off security features in the browser, which I don't want to do as a matter of principle).
The application uses a SQLite database. I want to provide the database to a sql.js constructor, which requires it in Uint8Array format.
Since I can't use AJAX to load the database file, I could instead load it with <input type="file"> and FileReader.prototype.readAsArrayBuffer and convert the ArrayBuffer to a Uint8Array. And that's working with the following code:
input.addEventListener('change', function (changeEvent) {
var file = changeEvent.currentTarget.files[0];
var reader = new FileReader();
reader.addEventListener('load', function (loadEvent) {
var buffer = loadEvent.target.result;
var uint8Array = new Uint8Array(buffer);
var db = new sql.Database(uint8Array);
});
reader.readAsArrayBuffer(file);
});
However, <input type="file"> requires user interaction, which is tedious.
I thought I might be able to work around the no-AJAX limitation by using a build tool to convert my database file to a JavaScript object / string and generate a ".js" file providing the file contents, and then convert the file contents to a Uint8Array, somehow.
Psuedocode:
// In Node.js:
var fs = require('fs');
var sqliteDb = fs.readFileSync('path/to/sqlite.db');
var string = convertBufferToJsStringSomehow(sqliteDb);
fs.writeFileSync('build/db.js', 'var dbString = "' + string + '";');
// In the browser (assume "build/db.js" has been loaded via a <script> tag):
var uint8Array = convertStringToUint8ArraySomehow(dbString);
var db = new sql.Database(uint8Array);
In Node.js, I've tried the following:
var TextEncoder = require('text-encoding').TextEncoder;
var TextDecoder = require('text-encoding').TextEncoder;
var sql = require('sql.js');
var string = new TextDecoder('utf-8').decode(fs.readFileSync('path/to/sqlite.db'));
// At this point, I would write `string` to a ".js" file, but for
// the sake of determining if converting back to a Uint8Array
// would work, I'll continue in Node.js...
var uint8array = new TextEncoder().encode(string);
var db = new sql.Database(uint8array);
db.exec('SELECT * FROM tablename');
But when I do that, I get the error "Error: database disk image is malformed".
What am I doing wrong? Is this even possible? The database disk image isn't "malformed" when I load the same file via FileReader.
Using the following code, I was able to transfer the database file's contents to the browser:
// In Node.js:
var fs = require('fs');
var base64 = fs.readFileSync('path/to/sqlite.db', 'base64');
fs.writeFileSync('build/db.js', 'var dbString = "' + base64 + '";');
// In the browser (assume "build/db.js" has been loaded via a <script> tag):
function base64ToUint8Array (string) {
var raw = atob(string);
var rawLength = raw.length;
var array = new Uint8Array(new ArrayBuffer(rawLength));
for (var i = 0; i < rawLength; i += 1) {
array[i] = raw.charCodeAt(i);
}
return array;
}
var db = new sql.Database(base64ToUint8Array(dbString));
console.log(db.exec('SELECT * FROM tablename'));
And that's working with the following code:
input.addEventListener('change', function (changeEvent) {
var file = changeEvent.currentTarget.files[0];
var reader = new FileReader();
reader.addEventListener('load', function (loadEvent) {
var buffer = loadEvent.target.result;
var uint8Array = new Uint8Array(buffer);
var db = new sql.Database(uint8Array);
});
reader.readAsArrayBuffer(file);
});
However, <input type="file"> requires user interaction, which is
tedious.
Using current working approach would be less tedious than attempting to create workarounds. If user intends to use application, user can select file from their filesystem to run application.

window.btoa not working for PNG and JPEG files

I convert file that are uploaded on the GUI and decoded it on the server which is a node.js server. Following code works fine for PDF files etc. but does not work for Image files ( PNG and JPEG). Following is the code :-
var fileName = form.findField("attachment").fileInputEl.dom.files[0];
var fileReader = new FileReader();
fileReader.onload = function (olEvent) {
var payload = window.btoa(unescape(encodeURIComponent(olEvent.target.result)));
contactObj['file_content'] = payload;
contactObj['file_name'] = fileName.name;
contactObj['file_type'] = fileName.type;
contactObj['file_size'] = fileName.size;
contactObj['fileAttached'] = fileAttached;
me.postContactUs(contactObj);
};
fileReader.readAsBinaryString(fileName);
}
on the node server using an npm (base-64) to decode this. Any ideas??

Error reading binary file

I'm trying to read a binary file in JavaScript.
However, it is not returning anything. What's wrong with the command below?
function autenticarbiometria() {
var fileInput = document.getElementById('fileInput');
var file = fileInput.files[0];
var reader = new FileReader();
var campo = "";
var status = "f";
filereader.onload = function(e) {
var campo = reader.result;
document.getElementById('template').value = campo;
alert("CAMPO TAMANHO --> " + campo.length);
}
reader.readAsArrayBuffer(file);
...
An ArrayBuffer cannot be set as string. You need to convert the content to a string, or a Base-64 / hex representation if the binary data is not printable.
For text you can use the new TextDecoder object (may need polyfill in some browsers).
Example
var td = new TextDecoder("utf-8"); // or use utf-16 etc. depending on what you expect
var txt = td.decode(campo); // pass in the ArrayBuffer
Now txt can be set as string source for your element (if readable).
Also note that you have a local var of campo which overrides the parent var campo.
Should it not be:
var fr = new FileReader;
var txt = fr.readAsText(document.getElementById('fileInput').files[0]);
fr.onloadend = function(r){
console.log(r);
}

Categories

Resources