This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 5 years ago.
I´m using fileReader to read the content of all selected files. After reading them with the fileReader API, I append the content to the DOM. That works perfectly.
It creates one p element per file.
Now I want to store each file content to local storage as well. Unfortunately, It stores only the last item. What´s going wrong? Thank you for your tips.
JS
$("input[name='uploadFile[]']").on("change", function() {
var files = !!this.files ? this.files : [];
if (!files.length || !window.FileReader)
return;
for (var i = 0; i < files.length; i++) {
(function(file) {
var name = file.name;
var reader = new FileReader();
reader.onload = function(e) {
var textObject = event.target.result.replace(/\r/g, "\n");
var textHTML = event.target.result.replace(/\r/g, "<br/>");
var text = e.target.result;
var p = document.createElement("p");
p.innerHTML = textHTML;
$('#results').append(p);
localStorage.setItem('letter'+ i, JSON.stringify(textObject));
};
reader.readAsText(file, 'ISO-8859-1');
})(files[i]);
}
});
The problem is that, by the time the onload is fired, i has been changed by the loop. This means that localStorage.setItem('letter'+ i will always refer to the last element in the array. You actually have the correct fix already in place -- the immediately invoked function expression -- but you need to add i as a parameter as well.
(function(file, index) {
var name = file.name;
var reader = new FileReader();
reader.onload = function(e) {
var textObject = event.target.result.replace(/\r/g, "\n");
var textHTML = event.target.result.replace(/\r/g, "<br/>");
var text = e.target.result;
var p = document.createElement("p");
p.innerHTML = textHTML;
$('#results').append(p);
localStorage.setItem('letter'+ index, JSON.stringify(textObject));
};
reader.readAsText(file, 'ISO-8859-1');
})(files[i], i);
There is also another solution if you can use let
let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
Here is what it could look like:
for (let i = 0; i < files.length; i++) {
let file = files[i];
let name = file.name;
let reader = new FileReader();
reader.onload = function(e) {
var textObject = e.target.result.replace(/\r/g, "\n");
var textHTML = e.target.result.replace(/\r/g, "<br/>");
var text = e.target.result;
var p = document.createElement("p");
p.innerHTML = textHTML;
$('#results').append(p);
localStorage.setItem('letter'+ i, JSON.stringify(textObject));
};
reader.readAsText(file, 'ISO-8859-1');
}
Related
Could anybody help me out sorting the following code or help me in the right direction?
It needs to import data from a .txt file and store it into localstorage as key & value.
Key is before ':' and value comes after it. A new key / value is separated after each ','.
Sample data from .txt file is:
nl-step1chapter1Question6:U2FsdGVkX19bRT84xShxK+29ypgj1d6ZHt+2DVBCUtY=,nl-step1chapter1Question1:U2FsdGVkX1+/Sv61L69bLvQGTkf1A9Uy4jgJ3KZTkzI=,nl-step1chapter1Question4:U2FsdGVkX1+9SVVOvTKeZuaQGj58L5WnEgL8htS0c7U=,jft:320982da-f32a-46a2-a97c-605ebe305518,nl-step1chapter1Question5:U2FsdGVkX19pi8A+PQZ7rBNCWrFeCwl2HdXpV+wWkFk=,nl-step1chapter1Question2:U2FsdGVkX19hnRnpmP3omzYNU0jXd3NtsHM+mvGYBnc=,nl-step1chapter1Question3:U2FsdGVkX1+hPbMRN+x19y7pF73eXoxG0qK1igZYZbA=
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="application/x-javascript">
$(function importData() {
document.getElementById('file').onchange = function () {
//debugger;
var file = this.files[0];
var reader = new FileReader();
reader.onload = function (progressEvent) {
//console.log(this.result.split(','));
var lines = this.result.split(',');
var list = [];
for (var line = 0; line < lines.length; line++) {
list.push(lines[line]);
localStorage.setItem([line],lines);
}
};
reader.readAsText(file);
};
});
</script>
Any help is much appreciated!
The way you are using FileReader doesn't seem correct to me. This is how your importData() function should be:
$(function importData() {
document.getElementById('file').onchange = function (event) {
var input = event.target;
var reader = new FileReader();
reader.onload = function () {
var text = reader.result;
var lines = text.split(',');
for (var line = 0; line < lines.length; line++) {
let elements = lines[line].split(':');
localStorage.setItem(elements[0], elements[1]);
}
};
reader.readAsText(input.files[0]);
};
});
It will insert the elements in the localStorage as you described. For example: key = step1chapter1Question1 and value = U2FsdGVkX1+/Sv61L69bLvQGTkf1A9Uy4jgJ3KZTkzI=
I followed the demo here:
https://github.com/SheetJS/js-xlsx/tree/master/demos/electron
I'm able to drag an excel file into my electron app.
The documentation says, you can access every cell with:
for(var R = range.s.r; R <= range.e.r; ++R) {
for(var C = range.s.c; C <= range.e.c; ++C) {
var cell_address = {c:C, r:R};
/* if an A1-style address is needed, encode the address */
var cell_ref = XLSX.utils.encode_cell(cell_address);
}
}
How do I use it with my Code below? I got the content of the file stored in my test variable, but I'm not able to access it. The documentation lack of information there.
var do_file = (function() {
return function do_file(files) {
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
data = new Uint8Array(data);
test = XLSX.read(data, {type: 'array'});
console.log(test);
};
reader.readAsArrayBuffer(f);
};
})();
I got no clue how to start with it, thanks in advance
Here it goes:
var do_file = (function() {
return function do_file(files) {
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
data = new Uint8Array(data);
//process_wb(XLSX.read(data, {type: 'array'}));
/* read the file */
var workbook = XLSX.read(data, {type: 'array'}); // parse the file
var sheet = workbook.Sheets[workbook.SheetNames[0]]; // get the first worksheet
/* loop through every cell manually */
var range = XLSX.utils.decode_range(sheet['!ref']); // get the range
for(var R = range.s.r; R <= range.e.r; ++R) {
for(var C = range.s.c; C <= range.e.c; ++C) {
/* find the cell object */
console.log('Row : ' + R);
console.log('Column : ' + C);
var cellref = XLSX.utils.encode_cell({c:C, r:R}); // construct A1 reference for cell
if(!sheet[cellref]) continue; // if cell doesn't exist, move on
var cell = sheet[cellref];
console.log(cell.v);
};
reader.readAsArrayBuffer(f);
};
})();
Generally you can access a cell with standard excel coordinates like
console.log(sheet['My Sheet Name']['B3'].v);
See the full data types here: https://github.com/SheetJS/sheetjs/blob/master/README.md#cell-object
So, as part of a little experiment, I need to access the binary data of a file, an image for example, as a string or array of 1s and 0s.
I read the documentation on the file reader, I'm attempting to use the readAsBinaryString method but it gives me a bunch of weird characters rather than what I'd normally think a string of binary should look like. Here's my code:
function handleFiles(files){
var selectedFile = files[0];
var reader = new FileReader();
reader.readAsBinaryString(selectedFile);
reader.onloadend = function () {
var result = reader.result;
store(result);
}
}
function store(data){
console.log('Storing data...');
console.log(data.slice(0, 1000));
}
As a web developer, I dont normally work with binary, so I'm probably fairly naive about this. Can someone explain how I get actual 1s and 0s?
I think I figured it out:
function handleFiles(files) {
var selectedFile = files[0];
var reader = new FileReader();
reader.readAsBinaryString(selectedFile);
reader.onloadend = function() {
var result = reader.result;
store(result);
};
}
function store(data) {
console.log("Storing data...");
var result = '';
data = data.slice(0, 1000)
for (var i1 = 0; i1 < data.length; i1++) {
result += data[i1].charCodeAt(0).toString(2) + " ";
}
console.log(result);
}
I was wondering if it is possible to get this right. I am using a Jquery cropping tool (cropit). I am trying to put multiple file inputs into several cropper instances at once. Unfortunately it doesnt work that well.
For example, if I upload three images, the first two croppers are empty and the last one gets one of the images randomly. Here is the function:
function handleCropit(files) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
var count = counterCropit();
var reader = new FileReader();
reader.onload= function(e){ $('#image-cropper'+count).cropit('imageSrc', e.target.result);};
reader.readAsDataURL(file);
}
}
Figured it out!
function handleCropit(files) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
var count = counterCropit();
var croper = $('#image-cropper'+count);
var reader = new FileReader();
reader.onload= (function (yo) {return function(e){ yo.cropit('imageSrc', e.target.result);}})(croper);
reader.readAsDataURL(file);
}
}
I am trying to crop-resize n display an image on client-browser using JS. I am able to do so except that my text loop is wrong. You can see in the image below that it is repeating the last iteration. My image loop works fine though. Hint - First filename text is supposed to be black.jpg.
Am unable to solve the issue after having tried for several hours. Given below is a trimmed version of the code. If needed here is the complete version of the script. Please help, I am still learning.
HTML
<input type="file" id="input" onchange="prevCrop()" multiple>
<ul id="listWrapper"></ul>
JAVASCRIPT
function prevCrop() {
var files = document.querySelector('input[type=file]').files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var fileSize = Math.floor(file.size/1024);
var info = file.name + " " + fileSize + " KB : iteration number - " + i;
var reader = new FileReader();
if (reader != null) {
reader.onload = GetThumbnail;
reader.readAsDataURL(file);
}
}
function GetThumbnail(e) {
var canvas = document.createElement("canvas");
var newImage = new Image();
newImage.src = e.target.result;
newImage.onload = cropResize;
function cropResize() {
canvas.width = 70;
canvas.height = 70;
### more codes here that handles image ###
var dataURL = canvas.toDataURL("image/jpg");
var thumbList = document.createElement('div');
thumbList.setAttribute('class', 'tlistClass');
var nImg = document.createElement('img');
nImg.src = dataURL;
var infoSpan = document.createElement('span');
infoSpan.innerHTML = info;
var handle = document.getElementById("listWrapper").appendChild(thumbList);
handle.appendChild(nImg);
handle.appendChild(infoSpan);
}
}
}
This happens because the onload callback function is triggered asynchronously, so after your code has already ended. At that time info has the string that was assigned to it in the last iteration. Only then the onload events are fired, resulting in the different calls of GetThumbnail, which all will see the same value for info.
Instead bind the value of info to the callback function like this:
reader.onload = GetThumbnail.bind(null, info);
...and define the corresponding parameter for it in the GetThumbnail function:
function GetThumbnail(info, e) {
That way every call of that function will posses of the proper value for info.