reader.onload function cant read value neither call function - javascript

im using reader.onload event to get contents of csv file,
problem is the value displays in console.log() but not in DOM i.e via binding
dropped(event: UploadEvent) {
this.files = event.files;
console.log(this.files)
for (const droppedFile of event.files) {
// Is it a file?
if (droppedFile.fileEntry.isFile) {
const fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
fileEntry.file((file: File) => {
// Here you can access the real file
console.log(droppedFile.relativePath, file);
const fileToRead = file;
const fileReader = new FileReader();
fileReader.onload = this.onFileLoad;
fileReader.readAsText(fileToRead);
console.log(this.tempval) /// undefined
});
}
}}
and onFileLoad function is as follows
onFileLoad(fileLoadedEvent) {
const textFromFileLoaded = fileLoadedEvent.target.result;
this.csvContent = textFromFileLoaded;
var allTextLines = this.csvContent.split(/\r\n|\n/);
var headers = allTextLines[0].split(',');
var lines = [];
for ( var i = 0; i < allTextLines.length; i++) {
// split content based on comma
var data = allTextLines[i].split(',');
if (data.length == headers.length) {
var tarr = [];
for ( var j = 0; j < headers.length; j++) {
tarr.push(data[j]);
}
lines.push(tarr);
}
}this.tempval = linesconsole.log(this.tempval) // printing value
};
unfortunately data inside this.tempval is not accessible anywhere
not in html DOM or inside dropped() funtion. except inside onFileLoad()
im just new to typescript
thanks in advance

Related

JS: How to take new image value from input

I'm trying to get an image from input convert it to array as well to display the new image into the imgPicture.src. However, I'm either getting undefined or empty source. Any possible solution? Thank you in advance.
let changePicInput = document.createElement("input");
changePicInput.type = "file";
changePicInput.id = `file-${finalArray[i].Id}`;
changePicInput.style.display = "none";
changePicInput.addEventListener("change", function () {
let arrBinaryFile = [];
let file = document.getElementById(`file-${materialId}`).files[0];
let reader = new FileReader();
// Array
reader.readAsArrayBuffer(file);
reader.onloadend = function (evt) {
if (evt.target.readyState == FileReader.DONE) {
var arrayBuffer = evt.target.result,
array = new Uint8Array(arrayBuffer);
for (var i = 0; i < array.length; i++) {
arrBinaryFile.push(array[i]);
}
}
}
// Display the image rightaway
imgPicture.src = file.value;
});
Hope it helps!
let imgPicture = document.querySelector('#imgPicture'); // Added the line.
let changePicInput = document.createElement("input");
changePicInput.type = "file";
changePicInput.id = `file-565656`; // Changed the line.
changePicInput.style.display = "block"; // Changed the line.
document.body.appendChild(changePicInput); // Added the line.
changePicInput.addEventListener("change", function () {
let arrBinaryFile = [];
let file = document.getElementById(`file-565656`).files[0]; // Changed the line.
let reader = new FileReader();
// Array
reader.readAsArrayBuffer(file);
reader.onloadend = function (evt) {
if (evt.target.readyState == FileReader.DONE) {
var arrayBuffer = evt.target.result,
array = new Uint8Array(arrayBuffer);
for (var i = 0; i < array.length; i++) {
arrBinaryFile.push(array[i]);
}
}
}
// Display the image rightaway
//imgPicture.src = file.value;
imgPicture.src = URL.createObjectURL(file) // Added the line.
console.log(file); // Added the line.
});
<body>
<img id="imgPicture">
</body>

Loop over uploaded files and return an array of file signatures

I want to loop over files selected for upload, get the file signature and return a array of file signatures. The listOfFileSignatures array is empty outside the readFirstFourBytes function. Is their a way to make it accessible globally?
var listOfFileSignatures = [];
var totalSize;
var uploadedFiles = document.getElementById("notes").files;
for (file of uploadedFiles) {
var blob = file;
var fileReader = new FileReader();
fileReader.onloadend = function readFirstFourBytes(e) {
var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
var fileSignature = "";
for (var i = 0; i < arr.length; i++) {
fileSignature += arr[i].toString(16);
};
listOfFileSignatures.push(fileSignature);
console.log(listOfFileSignatures); // Array(3) [ "ffd8ffdb", "ffd8ffe0", "47494638" ]
};
fileReader.readAsArrayBuffer(blob);
};
console.log(listOfFileSignatures); // Array []
Heres the output
You can declare listOfFileSignatures globally, but the signatures are computed asynchronously, so the list will be empty directly after the for loop. FileReader is always asynchronous, so you can't avoid that. One possibility to handle this is to check if the list is full inside onloadend (listOfFileSignatures.length == uploadedFiles.length) and then do what you want there.
A nicer approach is to use promises, like this:
var uploadedFiles = document.getElementById("notes").files;
Promise.all([...uploadedFiles].map(file => new Promise((resolve, reject) => {
var blob = file;
var fileReader = new FileReader();
fileReader.onloadend = function readFirstFourBytes(e) {
var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
var fileSignature = "";
for (var i = 0; i < arr.length; i++) {
fileSignature += arr[i].toString(16);
};
resolve(fileSignature);
};
fileReader.readAsArrayBuffer(blob);
}))).then(function(listOfFileSignatures) {
// this will be called once, when all results are collected.
console.log(listOfFileSignatures);
});
Additionally, reading all bytes and then select just the first 4 byte is inefficient. Improved version:
Promise.all([...uploadedFiles].map(file => new Promise((resolve, reject) => {
var blob = file;
var fileReader = new FileReader();
fileReader.onloadend = function readFirstFourBytes(e) {
var arr = new Uint8Array(e.target.result);
var fileSignature = "";
for (var i = 0; i < arr.length; i++) {
fileSignature += arr[i].toString(16);
};
resolve(fileSignature);
};
fileReader.readAsArrayBuffer(blob.slice(0, 4));
}))).then(function(listOfFileSignatures) {
// this will be called once, when all results are collected.
console.log(listOfFileSignatures);
});
fileReader.onload is asynchronous, console.log (listOfFileSignatures); is called before files have been read
one option is to create an external function that returns a promise, returning the listOfFileSignatures array
function getListFile() {
return new Promise((resolve, reject) => {
var blob = file;
var fileReader = new FileReader();
fileReader.onloadend = function readFirstFourBytes(e) {
var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
var fileSignature = "";
for (var i = 0; i < arr.length; i++) {
fileSignature += arr[i].toString(16);
};
listOfFileSignatures.push(fileSignature);
resolve(listOfFileSignatures);
};
}
}

FileReader not working in Angular 2

I'm trying to upload an image using FileReader, It works fine in debug mode (when set breakpoint on the line this.name = Image.files[0].name;), but doesn't work if I deactivate breakpoint. testDetails.image gets set to empty string. I have tried setTimeout also, its also not working.
var fileByteArray = '';
const Image = this.AccUserImage.nativeElement;
if (Image.files && Image.files[0]) {
this.AccUserImageFile = Image.files[0];
}
var fileReader = new FileReader();
fileReader.onload = function (event) {
var imageData = fileReader.result;
var bytes = new Uint8Array(imageData);
//for (var i = 0; i < bytes.length; i++) {
for (var i = 0; i < bytes.length; ++i) {
fileByteArray += (String.fromCharCode(bytes[i]));
}
};
if (fileReader && Image.files && Image.files.length) {
fileReader.readAsArrayBuffer(Image.files[0]);
}
}
this.name = Image.files[0].name;
const ImageFile: File = this.AccUserImageFile;
let length = this.form.value.addresses.length;
this.testList = [];
for (let i = 0; i < length; i++) {
let testDetails = new testDto();
testDetails.image = btoa(fileByteArray);
}
Maybe the test should be at the end of the fileReader.load function because your test depends on fileReader.onload function to be finished at least once so fileByteArray is not undefined.
fileReader.onload = function (event) {
var imageData = fileReader.result;
var bytes = new Uint8Array(imageData);
for (var i = 0; i < bytes.length; ++i) {
fileByteArray += (String.fromCharCode(bytes[i]));
}
if (fileReader && Image.files && Image.files.length) {
fileReader.readAsArrayBuffer(Image.files[0]);
}
for (let i = 0; i < length; i++) {
let testDetails = new testDto();
testDetails.image = btoa(fileByteArray);
}
};
There were some problems in the current implementation, I am posting the working code below. The first problem was that I was using JavaScript style calling for onload function. The second problem was I have to put all the code inside onload function because readAsArrayBuffer is an async call.
var fileByteArray = '';
const Image = this.AccUserImage.nativeElement;
if (Image.files && Image.files[0]) {
this.AccUserImageFile = Image.files[0];
}
var fileReader = new FileReader();
fileReader.onload = (e) => {
var imageData = fileReader.result;
var bytes = new Uint8Array(imageData);
for (var i = 0; i < bytes.length; ++i) {
fileByteArray += (String.fromCharCode(bytes[i]));
}
this.name = Image.files[0].name;
const ImageFile: File = this.AccUserImageFile;
let length = this.form.value.addresses.length;
this.testList = [];
for (let i = 0; i < length; i++) {
testDetails.image = btoa(fileByteArray);
}
}
fileReader.readAsArrayBuffer(Image.files[0]);

How to log contents of HTML5 drag and drop file that is 60MB+ without hanging for minutes?

I have a file that i want to drop on a page and read file contents. its a CSV with 9 columns. My drop command outputs file contents like this:
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.files[0];
var fileReader = new FileReader();
fileReader.onload = function (e) {
console.log(fileReader.result)
};
fileReader.onerror = function (e) {
throw 'Error reading CSV file';
};
// Start reading file
fileReader.readAsText(data);
return false;
}
When I drag and drop a simple file that is a couple kilobytes or 1MB, I can see the output of the contents of the file. However given a large CSV file, it takes many many minutes before it shows up. Is there a way to make it so that there is some streaming maybe where it does not look like its hanging?
With Screw-FileReader
You can get a ReadableStream and do it in a streaming fashion
'use strict'
var blob = new Blob(['111,222,333\naaa,bbb,ccc']) // simulate a file
var stream = blob.stream()
var reader = stream.getReader()
var headerString = ''
var forEachLine = function(row) {
var colums = row.split(',')
// append to DOM
console.log(colums)
}
var pump = function() {
return reader.read().then(function(result) {
var value = result.value
var done = result.done
if (done) {
// Do the last line
headerString && forEachLine(headerString)
return
}
for (var i = 0; i < value.length; i++) {
// Get the character for the current iteration
var char = String.fromCharCode(value[i])
// Check if the char is a new line
if (char.match(/[^\r\n]+/g) !== null) {
// Not a new line so lets append it to
// our header string and keep processing
headerString += char
} else {
// We found a new line character
forEachLine(headerString)
headerString = ''
}
}
return pump()
})
}
pump().then(function() {
console.log('done reading the csv')
})
<script src="https://cdn.rawgit.com/jimmywarting/Screw-FileReader/master/index.js"></script>
If you prefer using the old FileReader without dependencies, pipe's and transform
'use strict'
var blob = new Blob(['111,222,333\naaa,bbb,ccc']) // simulate a file
var fr = new FileReader()
var headerString = ''
var position = 0
var forEachLine = function forEachLine(row) {
var colums = row.split(',')
// append to DOM
console.log(colums)
}
var pump = function pump() {
return new Promise(function(resolve) {
var chunk = blob.slice(position, position + 524288)
position += 524288
fr.onload = function() {
var value = fr.result
var done = position >= blob.size
for (var i = 0; i < value.length; i++) {
var char = value[i]
// Check if the char is a new line
if (char.match(/[^\r\n]+/g) !== null) {
// Not a new line so lets append it to
// our header string and keep processing
headerString += char
} else {
// We found a new line character
forEachLine(headerString)
headerString = ''
}
}
if (done) {
// Send the last line
forEachLine(headerString)
return resolve() // done
}
return resolve(pump())
}
// Read the next chunk
fr.readAsText(chunk)
})
}
pump().then(function() {
console.log('done reading the csv')
})

Returning a variable from closure

How can I get the return value of that.whatever back from the closure? Instead of:
this.setCanvas = function(files){
var numItems = files.length - 1;
this.items = {};
var i = 0;
for(i=0;i<=numItems;i++)
{
var file = files[i];
var reader = new FileReader();
reader.onload = (function(i) {
return function(e) {
var something = that.whatever();
items[i] = something;
};
})(i);
reader.readAsDataURL(file);
}
console.log(items);
}
I need items[i] defined. If I console.log items[i] outside of the closure it is undefined.
The problem isn't the closure; it's the callback. Whatever needs to use the value of that.whatever needs to be executed in the callback.
You can augment your code to keep track of the number of files loaded. This way, when the last file has been loaded, you can invoke an ultimate completion handler:
this.setCanvas = function(files) {
var numItems = files.length - 1;
var itemsLoaded = 0; // Initialize to zero
var items = [];
var i = 0;
for(i=0;i<=numItems;i++) {
var file = files[i];
var reader = new FileReader();
reader.onload = (function(i) {
return function(e) {
var something = that.whatever();
items[i] = something;
if(++itemsLoaded == numItems) {
// At this point all files will have been loaded.
allLoaded();
}
};
})(i);
reader.readAsDataURL(file);
}
function allLoaded() {
// Now we can analyze the results
console.log(items);
}
}
I also changed items to be an Array instead of an Object.
Also if you want to be a little more clever, you could decrement numItems and check for zero instead of creating a new itemsLoaded variable.
There are a couple things you need to change here. First set this.items to an array. Next assign this to self so it can be referenced in the closure. Next assign the something to self.items[i] instead of items[i]. Finally use this.items in the console.log
this.setCanvas = function(files){
var self = this;
var numItems = files.length - 1;
this.items = [];
var i = 0;
for(i=0;i<=numItems;i++)
{
var file = files[i];
var reader = new FileReader();
reader.onload = (function(i) {
return function(e) {
var something = that.whatever();
self.items[i] = something;
};
})(i);
reader.readAsDataURL(file);
}
console.log(this.items);
}

Categories

Resources