XMLHttpRequest: Access object created in oReq.onload - javascript

I am using an XMLHttpRequest and sheetjs (https://github.com/SheetJS/js-xlsx) to create a json object from an excel spreadsheet. All is working well except I am struggling to access the created object outside of oReq.onload. After creating the json object I would like to use it as the input object to more code below the XMLHttpRequest.
I have tried to return json at the end of oReq.onload and also tried include my additional code inside function (e) {} but neither option has worked. Any suggestions would be appreciated.
/* set up XMLHttpRequest */
var url = "graph.xlsx";
var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
oReq.responseType = "arraybuffer";
oReq.onload = function(e) {
var arraybuffer = oReq.response;
/* convert data to binary string */
var data = new Uint8Array(arraybuffer);
var arr = new Array();
for(var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
var bstr = arr.join("");
/* Call XLSX */
var workbook = XLSX.read(bstr, {type:"binary"});
/* Create the json object */
var json = to_json(workbook);
/*Converts excel format to JSON*/
function to_json(workbook) {
var result = {};
workbook.SheetNames.forEach(function(sheetName) {
var roa = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[sheetName]);
if(roa.length > 0){
result[sheetName] = roa;
}
});
return result;
}
};
oReq.send();
/*I WOULD LIKE TO USE THE CREATED JSON OBJECT HERE*/

Ended up being an asynchronous problem as suggested by #epascarello. A very comprehensive explanation can be found in the post: Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference

using callback you can get the data outside the function, as is explained here https://stackoverflow.com/a/23667087/7981344
function asyncReq(callback){
oReq.onload = function(e) {
var arraybuffer = oReq.response;
var data = new Uint8Array(arraybuffer);
var arr = new Array();
for (var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
var bstr = arr.join("");
var workbook = XLSX.read(bstr, {
type: "binary"
});
var sheet_name = workbook.SheetNames[0];
var worksheet = workbook.Sheets[sheet_name];
myJson = XLSX.utils.sheet_to_json(worksheet, {
header: 1
});
callback(myJson);
}
}
oReq.send();
asyncReq(function(result){
//do whatever you want now with the output data
console.log(result);

hope this is not too late, but i came across the same problem.
just set a timeout, and wait for the onload function to finish
like this
setTimeout(function() {
console.log (yourvariable); //prints the variable outside the function
}, 1000);

Related

How to get a Header from xls/xlsx in JavaScript or jQuery?

How to get only header values from the xls/xlsx file in JavaScript or jQuery without parsing the whole file?. XLSX.read(data, { type: 'binary' }); taking too much time for parsing. But I need only headers on browser side. I'll do the rest on server-side. Thank you.
var url = "your excel file.xlsx";
var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
oReq.responseType = "arraybuffer";
oReq.onload = function(e) {
var arraybuffer = oReq.response;
var data = new Uint8Array(arraybuffer);
var arr = new Array();
for(var i = 0; i != data.length; ++i) {
arr[i] = String.fromCharCode(data[i]);
}
var bstr = arr.join("");
var workbook = XLSX.read(bstr, {type:"binary"});
var t = workbook.SheetNames;
}
oReq.send();

Push ArrayBuffer in Array for constructing a Blob

I've got an array of URLs [URL1, URL2, URL3,...] : each element is a link to one of the chunks of the same file. Each chunk is separately encrypted, with the same key as all the other chunks.
I download each chunk (in a forEach function) with a XMLHttpRequest. onload :
each chunk is first decrypted
then each chunk is converted to an ArrayBuffer (source)
each ArrayBuffer is pushed to an array (source)
when the three first steps are done for each chunk (callback by a var incremented on step#1 === the array.length), a blob is constructed with the array
the blob is saved as file with FileReader API & filesaver.js
If it's a one chunk's file, everything works fine.
But with multiple chunks, steps #1 & #2 are ok, but only the last ArrayBuffer seems to be pushed to the array. What am I missing?
Below my code
// var for incrementation in forEach funtion
var chunkdownloaded = 0;
// 'clearfileurl' is the array of url's chunks :[URL1, URL2, URL3,...]
clearfileurl.forEach(function(entry) {
var xhr = new XMLHttpRequest();
var started_at = new Date();
xhr.open('GET', entry, true);
xhr.responseType = 'text';
// request progress
xhr.onprogress = function(pe) {
if (pe.lengthComputable) {
downloaderval.set((pe.loaded / pe.total) * 100);
}
};
// on request's success
xhr.onload = function(e) {
if (this.status == 200) {
chunkdownloaded+=1;
var todecrypt = this.response;
// decrypt request's response: get a dataURI
try {
var bytesfile = CryptoJS.AES.decrypt(todecrypt.toString(), userKey);
var decryptedfile = bytesfile.toString(CryptoJS.enc.Utf8);
} catch(err) {
console.log (err);
return false;
}
//convert a dataURI to a Blob
var MyBlobBuilder = function() {
this.parts = [];
}
MyBlobBuilder.prototype.append = function(dataURI) {
//function dataURItoBlob(dataURI) {
// convert base64 to raw binary data held in a string
var byteString = atob(dataURI.split(',')[1]);
// separate out the mime component
// var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to an ArrayBuffer
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
this.parts.push(ab);
console.log('parts', this.parts)
this.blob = undefined; // Invalidate the blob
}
MyBlobBuilder.prototype.getBlob = function() {
if (!this.blob) {
console.log (this.parts);
this.blob = new Blob(this.parts);
}
return this.blob;
};
var myBlobBuilder = new MyBlobBuilder();
myBlobBuilder.append(decryptedfile);
// if all chunks are downloaded
if (chunkdownloaded === clearfileurl.length) {
// get the blob
var FinalFile = myBlobBuilder.getBlob();
// launch consturction of a file with'FinalFile' inside FileReader API
var reader = new FileReader();
reader.onload = function(e){
// build & save on client the final file with 'file-saver' library
var FileSaver = require('file-saver');
var file = new File([FinalFile], clearfilename, {type: clearfiletype});
FileSaver.saveAs(file);
};
reader.readAsText(FinalFile);
} else {
console.log('not yet');
}
}
};
// sending XMLHttpRequest
xhr.send();
});
You need to take out the declaration of MyBlobBuilder, try this:
// var for incrementation in forEach funtion
var chunkdownloaded = 0;
//convert a dataURI to a Blob
var MyBlobBuilder = function() {
this.parts = [];
}
MyBlobBuilder.prototype.append = function(dataURI, index) {
//function dataURItoBlob(dataURI) {
// convert base64 to raw binary data held in a string
var byteString = atob(dataURI.split(',')[1]);
// separate out the mime component
// var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to an ArrayBuffer
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
this.parts[index] = ab;
console.log('parts', this.parts)
this.blob = undefined; // Invalidate the blob
}
MyBlobBuilder.prototype.getBlob = function() {
if (!this.blob) {
console.log (this.parts);
this.blob = new Blob(this.parts);
}
return this.blob;
};
var myBlobBuilder = new MyBlobBuilder();
// 'clearfileurl' is the array of url's chunks :[URL1, URL2, URL3,...]
clearfileurl.forEach(function(entry, index) {
var xhr = new XMLHttpRequest();
var started_at = new Date();
xhr.open('GET', entry, true);
xhr.responseType = 'text';
// request progress
xhr.onprogress = function(pe) {
if (pe.lengthComputable) {
downloaderval.set((pe.loaded / pe.total) * 100);
}
};
// on request's success
xhr.onload = function(e) {
if (this.status == 200) {
chunkdownloaded+=1;
var todecrypt = this.response;
// decrypt request's response: get a dataURI
try {
var bytesfile = CryptoJS.AES.decrypt(todecrypt.toString(), userKey);
var decryptedfile = bytesfile.toString(CryptoJS.enc.Utf8);
} catch(err) {
console.log (err);
return false;
}
myBlobBuilder.append(decryptedfile, index);
// if all chunks are downloaded
if (chunkdownloaded === clearfileurl.length) {
// get the blob
var FinalFile = myBlobBuilder.getBlob();
// launch consturction of a file with'FinalFile' inside FileReader API
var reader = new FileReader();
reader.onload = function(e){
// build & save on client the final file with 'file-saver' library
var FileSaver = require('file-saver');
var file = new File([FinalFile], clearfilename, {type: clearfiletype});
FileSaver.saveAs(file);
};
reader.readAsText(FinalFile);
} else {
console.log('not yet');
}
}
};
// sending XMLHttpRequest
xhr.send();
});
*edit I also updated the append function to ensure that the files are in the correct order

js-xlsx Uncaught TypeError: console.log is not a function

I am using xlsx for reading Excel file. It kinda works... At least first row. I don't know what's the problem so here I am.
this is my code:
/* set up XMLHttpRequest */
var url = "http://localhost/test.xlsx";
var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
oReq.responseType = "arraybuffer";
oReq.onload = function(e) {
var arraybuffer = oReq.response;
/* convert data to binary string */
var data = new Uint8Array(arraybuffer);
var arr = new Array();
for(var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
var bstr = arr.join("");
/* Call XLSX */
var workbook = XLSX.read(bstr, {type:"binary"});
/* DO SOMETHING WITH workbook HERE */
var alphabet = "ABC".split("");
var first_sheet_name = workbook.SheetNames[0];
/* Get worksheet */
var worksheet = workbook.Sheets[first_sheet_name];
var address_of_cell, desired_cell, desired_value;
var descript;
for (i=1;i<30;i++) {
for (j=0;j<alphabet.length;j++) {
if (alphabet[j] == "A") {
console.log("This will be title: "+getValue(alphabet[j], i));
} else if (alphabet[j] == "B") {
descript = getValue(alphabet[j], i);
console.log(""+descript);
} else if (alphabet[j] == "C") {
console.log="This will be description: " + descript+" - "+getValue(alphabet[j], i);
}
}
}
function getValue (column, row) {
address_of_cell = column+''+row;
console.log(address_of_cell);
/* Find desired cell */
desired_cell = worksheet[address_of_cell];
/* Get the value */
desired_value = desired_cell.v;
return(desired_value);
}
};
oReq.send();
Now the result I get in console is:
A1
This will be title: VI/46/1998
B1
30-12-1998
C1
Uncaught TypeError: console.log is not a function Bookmarklet.js:43
As You can see in C1 I get that console.log is not a fucntion, but why? Where's my mistake? What am I doing wrong?
Sincerely,
Thomas
console.log is provided by the environment in which your code runs. Or not. It depends on the environment. Modern browsers provide it (at least if you have devtools open; some versions of IE don't provide it if you don't). NodeJS provides it.
Two possibilities:
Your environment doesn't provide it.
It does, but then you run this line of code:
console.log="This will be description: " + descript+" - "+getValue(alphabet[j], i);
which overwrites it with a string. Strings aren't functions, so the attempt to call it on any of your lines that use it correctly (like console.log(address_of_cell);) will now start failing.
That line should be like the others, a function call:
console.log("This will be description: " + descript+" - "+getValue(alphabet[j], i));

Parsing Excel sheet with js-xlsx

I am trying to parse all the excel files in a directory specified by the user but the js-xlsx library I am using seems to need manual navigation.
var url = "/test-files/test.xlsx"; <-------- Located in the project directory
var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
oReq.responseType = "arraybuffer";
oReq.onload = function(e) {
var arraybuffer = oReq.response;
var data = new Uint8Array(arraybuffer);
var arr = new Array();
for (var i = 0; i != data.length; i++) arr[i] = String.fromCharCode(data[i]);
var bstr = arr.join("");
var workbook = XLSX.read(bstr, {
type: "binary"
});
}
oReq.send();
The code needs to be dynamic in that it can open an excel file anywhere.
Is there any way that I can use a fileentry object with js-xlsx library to parse an excel file?
For those who may be curious I figured out a solution. In order to dynamically create a path to a given fileEntry object you must first convert it into a Blob:
fileEntryObject.file(function(file) {});
Then convert it to a window.URL that way your project may have access to a readable path to the desired file:
var newPath = window.URL.createObjectURL(file);
Thus you may use it like a regular path in your functions even if you don't know how to navigate from your project to the file:
fileEntryObject.file(function(file) {
var newPath = window.URL.createObjectURL(file);
var oReq = new XMLHttpRequest();
oReq.open("GET", newPath, true);
oReq.responseType = "arraybuffer";
oReq.onError = function(e) {
console.log('Error in reading excel file');
};
oReq.onload = function(e) {
var arraybuffer = oReq.response;
var data = new Uint8Array(arraybuffer);
var arr = new Array();
for (var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
var bstr = arr.join("");
var workbook = XLSX.read(bstr, {
type: "binary"
});
var sheet_name = workbook.SheetNames[1];
var worksheet = workbook.Sheets[sheet_name];
self.parseReceive(worksheet, callback);
// self.parseReceive(worksheet);
};
oReq.send();
});

JavaScript readAsBinaryString Function on E11

In this page http://www.html5rocks.com/en/tutorials/file/dndfiles/ if you scroll down to example "Example: Slicing a file. Try it!" you will see uses of readAsBinaryString API to read bytes of local files.
I've seen IE (My case its IE11) doesn't support readAsBinaryString.
Even this code mentioned in post HTML5 File API read as text and binary breaks at readAsBinaryString in IE11.
I have seen some post in stack overflow, it suggests use of ReadAsArrayBuffer(). But it is also not working. It returns undefined.
My question is what are the options if I have to run it on IE11? Is it possible to write another IE compatible JS function which will do the JOB of readAsBinaryString().
I combine #Jack answer with my comment to show a complete working example.
In the <head> section I added this script to add FileReader.readAsBinaryString function in IE11
if (FileReader.prototype.readAsBinaryString === undefined) {
FileReader.prototype.readAsBinaryString = function (fileData) {
var binary = "";
var pt = this;
var reader = new FileReader();
reader.onload = function (e) {
var bytes = new Uint8Array(reader.result);
var length = bytes.byteLength;
for (var i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i]);
}
//pt.result - readonly so assign content to another property
pt.content = binary;
pt.onload(); // thanks to #Denis comment
}
reader.readAsArrayBuffer(fileData);
}
}
Then I needed to slightly modify my original script code because target.result has no value when using this fallback function.
var reader = new FileReader();
reader.onload = function (e) {
// ADDED CODE
if (!e) {
var data = reader.content;
}
else {
var data = e.target.result;
}
// business code
};
reader.readAsBinaryString(myFile);
This is my solution.
var reader = new FileReader();
reader.readAsBinaryString(fileData);
reader.onload = function(e) {
if (reader.result) reader.content = reader.result;
var base64Data = btoa(reader.content);
//...
}
//extend FileReader
if (!FileReader.prototype.readAsBinaryString) {
FileReader.prototype.readAsBinaryString = function (fileData) {
var binary = "";
var pt = this;
var reader = new FileReader();
reader.onload = function (e) {
var bytes = new Uint8Array(reader.result);
var length = bytes.byteLength;
for (var i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i]);
}
//pt.result - readonly so assign binary
pt.content = binary;
$(pt).trigger('onload');
}
reader.readAsArrayBuffer(fileData);
}
}
FileReader.readAsBinaryString is a non-standard function and has been deprecated.
FileReader.readAsArrayBuffer should be used instead.
MDN
For IE 11 you can use this XHR trick:
function blobToBinaryStringIE11(blob) {
var blobURL = URL.createObjectURL(blob);
var xhr = new XMLHttpRequest;
xhr.open("get", blobURL);
xhr.overrideMimeType("text/plain; charset=x-user-defined");
xhr.onload = function () {
var binary = xhr.response;
// do stuff
};
xhr.send();
}
It's 20x faster than the Uint8Array + fromCharCode route and as fast as readAsBinaryString.
Replace
reader.readAsBinaryString(blob);
with:
reader.readAsText(blob);
it's works well in cross browser.
I had some problems with the answers here and ended up making a few slight changes.
Instead of assigning to pt.content, my solution is to send a custom object to the prototype's onload, that receiver can specifically look for, I named this property msieContent so it will be very specific.
Also I used other accepted answer for converting Uint8Array to string in more robust way, you can see full details of it here:
https://stackoverflow.com/a/12713326/213050
Polyfill
if (FileReader.prototype.readAsBinaryString === undefined) {
// https://stackoverflow.com/a/12713326/213050
function Uint8ToString(u8a: Uint8Array) {
const CHUNK_SZ = 0x8000;
let c = [];
for (let i = 0; i < u8a.length; i += CHUNK_SZ) {
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
}
return c.join('');
}
FileReader.prototype.readAsBinaryString = function (fileData) {
const reader = new FileReader();
reader.onload = () => this.onload({
msieContent: Uint8ToString(new Uint8Array(<any>reader.result))
});
reader.readAsArrayBuffer(fileData);
}
}
Usage
private _handleTextFile(file: File) {
const reader = new FileReader();
reader.onload = (e) => {
// support for msie, see polyfills.ts
const readResult: string = (<any>e).msieContent || <string>e.target.result;
};
reader.readAsBinaryString(file);
}

Categories

Resources