FileReader error : Cannot read property 'size' of undefined - javascript

Good Day,
I have this code:
function loadFile() {
var fileToLoad = document.getElementById("loadMe").files[0];
var fileStart = 0;
var fileEnd = fileToLoad.size - 1;
var fileReader = new FileReader();
fileReader.onload = function(fileLoadedEvent) {
var textFromFileLoaded = fileLoadedEvent.target.result;
var string = textFromFileLoaded.split('&');
pleaseDoSomethingWithTheContentOfTheFile(textFromFileLoaded);
};
fileReader.readAsText(fileToLoad, "UTF-8");
}
I have an error :
Uncaught TypeError: Cannot read property 'size' of undefined
at loadFile
According to this post there is no index [0]... But when I remove that, I still have the same error. Is this something coming from this code, or does it look good ?
Thanks in advance! :)

double check whether the file is selected before calling loadFile function.
also use below code checking the size
if (!fileToLoad) {
alert('Pleae select file');
return;
}

Related

Uncaught TypeError: this.files is undefined thrown by fr.readAsText(this.files[0]) (FileReader)

I am experiencing an unexpected error when trying to load a javascript object using FileReader, unexpected because it works fine in another project, which is listed below. My intention is to perform a few operations on the javascript object right after reading it by executing a function that has several parameters, and that's why I changed the code structure because I didn't know how to put parameters in the code that works. Any tips are welcome.
Code that doesn't work
function wczytajSklad(sklad, id, objSklad, gdzie) {
var fr = new FileReader();
fr.onload = function(){
let wczytanySklad = fr.result;
sklad = JSON.parse(wczytanySklad);
ukryjInputFile(id);
pokazCheckAndRadio(objSklad, gdzie);
}
fr.readAsText(this.files[0]);
}
A place where i try to run function
<input type="file" name="gospodarze" id="wczytajSkladGospodarzy" onchange="wczytajSklad(skladGospodarzy, 'wczytajSkladGospodarzy', skladGospodarzy, 'skladGospodarzy')">
Code that works
document.getElementById('inputfile')
.addEventListener('change', function() {
var fr = new FileReader();
fr.onload = function(){
wczytanyPlik = fr.result;
arrPoints = JSON.parse(wczytanyPlik);
rysuj();
}
fr.readAsText(this.files[0]);
})

Parsing error in a FileReader onload function

For some reason I am getting an error with my FileReader unload function. The error is Parsing error: Invalid left-hand side in assignment expression I looked in to what that means to according to MDN web docs: "invalid assignment left-hand side" occurs when there was an unexpected assignment somewhere. For example, a single "=" sign was used instead of "==" or "===". I don't see how this would be a error.
var fileInput = files[0];
var audioCtx = new AudioContext(files[0]);
var reader1 = new FileReader();
reader1.onload() = function (ev) { //error here
//decode audio
audioCtx.decodeAudioData(ev.target.result).then(function(buffer) {
reader1.readAsArrayBuffer(fileInput.files[0]);
})
}
onload is a property, not a method.
reader1.onload = function (ev) {
...
}

Getting byte array through input type = file

var profileImage = fileInputInByteArray;
$.ajax({
url: 'abc.com/',
type: 'POST',
dataType: 'json',
data: {
// Other data
ProfileImage: profileimage
// Other data
},
success: {
}
})
// Code in WebAPI
[HttpPost]
public HttpResponseMessage UpdateProfile([FromUri]UpdateProfileModel response) {
//...
return response;
}
public class UpdateProfileModel {
// ...
public byte[] ProfileImage {get ;set; }
// ...
}
<input type="file" id="inputFile" />
I am using ajax call to post byte[] value of a input type = file input to web api which receives in byte[] format. However, I am experiencing difficulty of getting byte array. I am expecting that we can get the byte array through File API.
Note: I need to store the byte array in a variable first before passing through ajax call
[Edit]
As noted in comments above, while still on some UA implementations, readAsBinaryString method didn't made its way to the specs and should not be used in production.
Instead, use readAsArrayBuffer and loop through it's buffer to get back the binary string :
document.querySelector('input').addEventListener('change', function() {
var reader = new FileReader();
reader.onload = function() {
var arrayBuffer = this.result,
array = new Uint8Array(arrayBuffer),
binaryString = String.fromCharCode.apply(null, array);
console.log(binaryString);
}
reader.readAsArrayBuffer(this.files[0]);
}, false);
<input type="file" />
<div id="result"></div>
For a more robust way to convert your arrayBuffer in binary string, you can refer to this answer.
[old answer] (modified)
Yes, the file API does provide a way to convert your File, in the <input type="file"/> to a binary string, thanks to the FileReader Object and its method readAsBinaryString.
[But don't use it in production !]
document.querySelector('input').addEventListener('change', function(){
var reader = new FileReader();
reader.onload = function(){
var binaryString = this.result;
document.querySelector('#result').innerHTML = binaryString;
}
reader.readAsBinaryString(this.files[0]);
}, false);
<input type="file"/>
<div id="result"></div>
If you want an array buffer, then you can use the readAsArrayBuffer() method :
document.querySelector('input').addEventListener('change', function(){
var reader = new FileReader();
reader.onload = function(){
var arrayBuffer = this.result;
console.log(arrayBuffer);
document.querySelector('#result').innerHTML = arrayBuffer + ' '+arrayBuffer.byteLength;
}
reader.readAsArrayBuffer(this.files[0]);
}, false);
<input type="file"/>
<div id="result"></div>
$(document).ready(function(){
(function (document) {
var input = document.getElementById("files"),
output = document.getElementById("result"),
fileData; // We need fileData to be visible to getBuffer.
// Eventhandler for file input.
function openfile(evt) {
var files = input.files;
// Pass the file to the blob, not the input[0].
fileData = new Blob([files[0]]);
// Pass getBuffer to promise.
var promise = new Promise(getBuffer);
// Wait for promise to be resolved, or log error.
promise.then(function(data) {
// Here you can pass the bytes to another function.
output.innerHTML = data.toString();
console.log(data);
}).catch(function(err) {
console.log('Error: ',err);
});
}
/*
Create a function which will be passed to the promise
and resolve it when FileReader has finished loading the file.
*/
function getBuffer(resolve) {
var reader = new FileReader();
reader.readAsArrayBuffer(fileData);
reader.onload = function() {
var arrayBuffer = reader.result
var bytes = new Uint8Array(arrayBuffer);
resolve(bytes);
}
}
// Eventlistener for file input.
input.addEventListener('change', openfile, false);
}(document));
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<input type="file" id="files"/>
<div id="result"></div>
</body>
</html>
Modern browsers now have the arrayBuffer method on Blob's:
document.querySelector('input').addEventListener('change', async (event) => {
const buffer = await event.target.files[0].arrayBuffer()
console.log(buffer)
}, false)
🎉 🎉
This is a long post, but I was tired of all these examples that weren't working for me because they used Promise objects or an errant this that has a different meaning when you are using Reactjs. My implementation was using a DropZone with reactjs, and I got the bytes using a framework similar to what is posted at this following site, when nothing else above would work: https://www.mokuji.me/article/drop-upload-tutorial-1 . There were 2 keys, for me:
You have to get the bytes from the event object, using and during a FileReader's onload function.
I tried various combinations, but in the end, what worked was:
const bytes = e.target.result.split('base64,')[1];
Where e is the event. React requires const, you could use var in plain Javascript. But that gave me the base64 encoded byte string.
So I'm just going to include the applicable lines for integrating this as if you were using React, because that's how I was building it, but try to also generalize this, and add comments where necessary, to make it applicable to a vanilla Javascript implementation - caveated that I did not use it like that in such a construct to test it.
These would be your bindings at the top, in your constructor, in a React framework (not relevant to a vanilla Javascript implementation):
this.uploadFile = this.uploadFile.bind(this);
this.processFile = this.processFile.bind(this);
this.errorHandler = this.errorHandler.bind(this);
this.progressHandler = this.progressHandler.bind(this);
And you'd have onDrop={this.uploadFile} in your DropZone element. If you were doing this without React, this is the equivalent of adding the onclick event handler you want to run when you click the "Upload File" button.
<button onclick="uploadFile(event);" value="Upload File" />
Then the function (applicable lines... I'll leave out my resetting my upload progress indicator, etc.):
uploadFile(event){
// This is for React, only
this.setState({
files: event,
});
console.log('File count: ' + this.state.files.length);
// You might check that the "event" has a file & assign it like this
// in vanilla Javascript:
// var files = event.target.files;
// if (!files && files.length > 0)
// files = (event.dataTransfer ? event.dataTransfer.files :
// event.originalEvent.dataTransfer.files);
// You cannot use "files" as a variable in React, however:
const in_files = this.state.files;
// iterate, if files length > 0
if (in_files.length > 0) {
for (let i = 0; i < in_files.length; i++) {
// use this, instead, for vanilla JS:
// for (var i = 0; i < files.length; i++) {
const a = i + 1;
console.log('in loop, pass: ' + a);
const f = in_files[i]; // or just files[i] in vanilla JS
const reader = new FileReader();
reader.onerror = this.errorHandler;
reader.onprogress = this.progressHandler;
reader.onload = this.processFile(f);
reader.readAsDataURL(f);
}
}
}
There was this question on that syntax, for vanilla JS, on how to get that file object:
JavaScript/HTML5/jQuery Drag-And-Drop Upload - "Uncaught TypeError: Cannot read property 'files' of undefined"
Note that React's DropZone will already put the File object into this.state.files for you, as long as you add files: [], to your this.state = { .... } in your constructor. I added syntax from an answer on that post on how to get your File object. It should work, or there are other posts there that can help. But all that Q/A told me was how to get the File object, not the blob data, itself. And even if I did fileData = new Blob([files[0]]); like in sebu's answer, which didn't include var with it for some reason, it didn't tell me how to read that blob's contents, and how to do it without a Promise object. So that's where the FileReader came in, though I actually tried and found I couldn't use their readAsArrayBuffer to any avail.
You will have to have the other functions that go along with this construct - one to handle onerror, one for onprogress (both shown farther below), and then the main one, onload, that actually does the work once a method on reader is invoked in that last line. Basically you are passing your event.dataTransfer.files[0] straight into that onload function, from what I can tell.
So the onload method calls my processFile() function (applicable lines, only):
processFile(theFile) {
return function(e) {
const bytes = e.target.result.split('base64,')[1];
}
}
And bytes should have the base64 bytes.
Additional functions:
errorHandler(e){
switch (e.target.error.code) {
case e.target.error.NOT_FOUND_ERR:
alert('File not found.');
break;
case e.target.error.NOT_READABLE_ERR:
alert('File is not readable.');
break;
case e.target.error.ABORT_ERR:
break; // no operation
default:
alert('An error occurred reading this file.');
break;
}
}
progressHandler(e) {
if (e.lengthComputable){
const loaded = Math.round((e.loaded / e.total) * 100);
let zeros = '';
// Percent loaded in string
if (loaded >= 0 && loaded < 10) {
zeros = '00';
}
else if (loaded < 100) {
zeros = '0';
}
// Display progress in 3-digits and increase bar length
document.getElementById("progress").textContent = zeros + loaded.toString();
document.getElementById("progressBar").style.width = loaded + '%';
}
}
And applicable progress indicator markup:
<table id="tblProgress">
<tbody>
<tr>
<td><b><span id="progress">000</span>%</b> <span className="progressBar"><span id="progressBar" /></span></td>
</tr>
</tbody>
</table>
And CSS:
.progressBar {
background-color: rgba(255, 255, 255, .1);
width: 100%;
height: 26px;
}
#progressBar {
background-color: rgba(87, 184, 208, .5);
content: '';
width: 0;
height: 26px;
}
EPILOGUE:
Inside processFile(), for some reason, I couldn't add bytes to a variable I carved out in this.state. So, instead, I set it directly to the variable, attachments, that was in my JSON object, RequestForm - the same object as my this.state was using. attachments is an array so I could push multiple files. It went like this:
const fileArray = [];
// Collect any existing attachments
if (RequestForm.state.attachments.length > 0) {
for (let i=0; i < RequestForm.state.attachments.length; i++) {
fileArray.push(RequestForm.state.attachments[i]);
}
}
// Add the new one to this.state
fileArray.push(bytes);
// Update the state
RequestForm.setState({
attachments: fileArray,
});
Then, because this.state already contained RequestForm:
this.stores = [
RequestForm,
]
I could reference it as this.state.attachments from there on out. React feature that isn't applicable in vanilla JS. You could build a similar construct in plain JavaScript with a global variable, and push, accordingly, however, much easier:
var fileArray = new Array(); // place at the top, before any functions
// Within your processFile():
var newFileArray = [];
if (fileArray.length > 0) {
for (var i=0; i < fileArray.length; i++) {
newFileArray.push(fileArray[i]);
}
}
// Add the new one
newFileArray.push(bytes);
// Now update the global variable
fileArray = newFileArray;
Then you always just reference fileArray, enumerate it for any file byte strings, e.g. var myBytes = fileArray[0]; for the first file.
This is simple way to convert files to Base64 and avoid "maximum call stack size exceeded at FileReader.reader.onload" with the file has big size.
document.querySelector('#fileInput').addEventListener('change', function () {
var reader = new FileReader();
var selectedFile = this.files[0];
reader.onload = function () {
var comma = this.result.indexOf(',');
var base64 = this.result.substr(comma + 1);
console.log(base64);
}
reader.readAsDataURL(selectedFile);
}, false);
<input id="fileInput" type="file" />
document.querySelector('input').addEventListener('change', function(){
var reader = new FileReader();
reader.onload = function(){
var arrayBuffer = this.result,
array = new Uint8Array(arrayBuffer),
binaryString = String.fromCharCode.apply(null, array);
console.log(binaryString);
console.log(arrayBuffer);
document.querySelector('#result').innerHTML = arrayBuffer + ' '+arrayBuffer.byteLength;
}
reader.readAsArrayBuffer(this.files[0]);
}, false);
<input type="file"/>
<div id="result"></div>
Here is one answer to get the actual final byte array , just using FileReader and ArrayBuffer :
const test_function = async () => {
... ... ...
const get_file_array = (file) => {
return new Promise((acc, err) => {
const reader = new FileReader();
reader.onload = (event) => { acc(event.target.result) };
reader.onerror = (err) => { err(err) };
reader.readAsArrayBuffer(file);
});
}
const temp = await get_file_array(files[0])
console.log('here we finally ve the file as a ArrayBuffer : ',temp);
const fileb = new Uint8Array(fileb)
... ... ...
}
where file is directly the File object u want to read , this has to be done in a async function...

cannot load image from the local storage using json

I am a beginner and trying to load local image using json on the browser.
I have 3 questions for this code:
var jsonString = '[{"1":"/models/view/css/pics/dipesh.jpg","2":"/models/view/css/pics/ashish.jpg","3":"/models/view/css/pics/prasun.jpg"}]';
localStorage.setItem("1", jsonString);
var obj = JSON.parse(localStorage.getItem("1"));
$(".player").click(function() {
var img = new Image(150, 150);
for (var i = 0; i < obj.length; i++) {
img.setAttribute("src", obj[i][1]);
document.getElementById(img).style.borderRadius = "8px";
$("#my").html(img);
}
})
<div class="shape" id="my"></div>
my 1st question is:
If I use web path its working. But it shows only 1st image.It is not loading other images.
Therefore I used localStorage to use local image path but its not working.It shows this error:
GET http://localhost:8080/undefined 404 (Not Found)
Question no.3 is, it is not setting the border radius of the image.nad showing this error:
Uncaught TypeError: Cannot read property 'style' of null
Can anybody help me?
Thanx in advance.
Your data is an array with just an object at first index. And it's already JSON :
var jsonString = '[{"1":"/models/view/css/pics/dipesh.jpg","2":"/models/view/css/pics/ashish.jpg","3":"/models/view/css/pics/prasun.jpg"}]';
var jsonObj = JSON.parse(jsonString);
for(var key in jsonObj[0]) {
console.log(jsonObj[0][key])
}
see updated code on fiddle
var jsonString = '[{"1":"https://www.google.co.in/images/srpr/logo11w.png","2":"https://www.google.co.in/images/srpr/logo11w.png","3":"https://www.google.co.in/images/srpr/logo11w.png"}]';
localStorage.setItem("1", jsonString);
var obj = JSON.parse(localStorage.getItem("1"));
$(".player").click(function() {
$.each(obj[0],function(key,value){
var img = new Image(150, 150);
img.setAttribute("src", value);
img.style.borderRadius = "8px";
$("#my").append(img);
});
});
Use $.parseJSON instead as follows,
var jsonString = '[{"1":"/models/view/css/pics/dipesh.jpg","2":"/models/view/css/pics/ashish.jpg","3":"/models/view/css/pics/prasun.jpg"}]';
var obj = $.parseJSON(jsonString);
for(var key in obj[0]) {
console.log(obj[0][key])
}

JavaScript: Making a subclass of Blob

I am experimenting to see if I can customize a Blob that I will pass to a FileReader. The goal of the sample code is to be able to programmatically
generate data on the fly for the FileReader to consume. However, it looks like I am not subclassing correctly as I get a TypeError when I try to use my custom class.
Here is my sample code:
// Example of reading a real blob
var realBlob = new Blob(['foo', 'bar']);
var reader1 = new FileReader();
reader1.onload = function(event){
console.log(JSON.stringify(reader1.result));
};
reader1.readAsText(realBlob);
// Writes "foobar"
// (non-working) streaming blob
function StreamingBlob() {}
StreamingBlob.prototype = new Blob;
StreamingBlob.prototype.constructor = StreamingBlob;
var bInst = new StreamingBlob();
bInst.slice = function (start, end) {
str = '';
while (start++ < end) {
str += 'A';
}
return new Blob([str]);
}
// Instance of says it's a Blob
console.log(bInst instanceof StreamingBlob);
console.log(bInst instanceof Blob);
console.log(bInst);
console.log(bInst.slice(1,5));
var reader2 = new FileReader();
reader2.onload = function(event){
console.log(JSON.stringify(reader2.result));
};
reader2.readAsText(bInst);
// Error!
When I run this I see:
true
true
StreamingBlob {slice: function, type: "", size: 0, constructor: function}
Blob {type: "", size: 4, slice: function}
Uncaught TypeError: Failed to execute 'readAsText' on 'FileReader': parameter 1 is not of type 'Blob'.
"foobar"
I'm confused, because the instanceof check claims that my object is a Blob, but the readAsText method generates an error that claims otherwise.
Is there a better way to do this?

Categories

Resources