HTML5 FileReader API onload event not called - javascript

I am trying to implement the FileReader API to read an audio file, but the script never gets to that point from what it seems. My function is below and the trouble is at the reader.onload = handleReaderLoad; line.
function setupFS(file) {
console.log('setupFS function entered.');
var reader = new FileReader();
console.log(reader);
reader.onload = handleReaderLoad;
}
function handleReaderLoad(evt) {
console.log(reader);
var audioSrc = $('.file-playlist table tr:nth-child(n+1) td:first-child');
console.log(audioSrc);
console.log(reader.readAsDataURL(file));
audioSrc.attr('data-src', reader.readAsDataURL(file));
}
In the console, the reader shows up with the onload event as having a function handleReaderLoad(evt) { call, but the reader, audioSrc, and reader.readAsDataURL(file) variables are never logged in the console.
What am I missing?

I've figured out how the FileReader API wants the events to be set up. The main process of using a FileReader works by creating a FileReader, then declaring any/all of its events such as the onload or the onloadend events which are shown below. The process can also be condensed into one main function.
function readFile(file) {
var audioSrc;
audioSrc = $('.file-playlist table tr:nth-child(' + n + ') td:first-child');
var progress = $('.file-playlist table tr:nth-child(' + n + ') td:last-child progress');
n++;
progress.removeAttr('value');
progress.attr('data-mod', 'true');
var reader = new FileReader();
reader.onload = (function(file) {
return function(e) {
audioSrc.attr('data-src', e.target.result);
$('.file-playlist audio source').attr('data-src', e.target.result);
progress.attr('value', '100');
console.log('onload stage finished');
};
})(file);
reader.onloadend = (function() {
audioSrc.text(file.name);
});
reader.readAsDataURL(file);
}
The function works by creating a FileReader, then declaring its onload events by returning the function, and the reader is given content by reading in data at the end of the function, in this case by using the readAsDataURL() method.

Related

Unable to preview image after converting attachment to Base64 using Trix

I'm using Trix, and for uploading attachments our backend developer tells me that I should convert the attachment to base64 and save the base64 data to the database instead of uploading the binary file.
I wrote this code for implementing it, and the output of the input field(HTML) is working as expected, but the image preview doesn't show in the editor.
$(function() {
$(document).on('trix-attachment-add', function(event) {
var attachment = event.originalEvent.attachment;
// Convert data URLs to File objects.
if(attachment.file) {
return uploadAttachment(attachment);
} else {
console.error('Could not upload attachment.');
console.error(attachment);
attachment.remove();
}
});
});
function uploadAttachment(attachment) {
var reader = new FileReader();
console.log(attachment)
// Set the reader to insert images when they are loaded.
reader.onload = function (e) {
var result = e.target.result;
var attrs = {
src : result,
url: result,
href : ''
};
attachment.setAttributes(attrs)
attachment.setUploadProgress(100);
}
// Read image as base64.
reader.readAsDataURL(attachment.file);
}
I don't know what causes this problem.
Try replacing
$(document).on('trix-attachment-add', function(event) {
with
document.addEventListener("trix-attachment-add", function(event) {
This could be event listeners being cached thus firing multiple times. The first load of image works, it could be the next loads that make this look busted.
Could also be Turbolinks issue so wrap your code with this instead:
$(document).on('turbolinks:load', function() {
I've managed to solve the issue by setting the fileObjectURL property as shown below
attachment.attachment.fileObjectURL = result;
Complete code for latest version (works with Symfony 6 easy admin bundle):
(function() {
function asBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
document.addEventListener("trix-file-accept", function(event) {
event.preventDefault();
if (event.file) {
asBase64(event.file).then(function(data) {
let image = document.createElement('img');
image.src = data;
let tmp = document.createElement('div');
tmp.appendChild(image);
let editor = document.querySelector('trix-editor');
editor.editor.insertHTML(tmp.innerHTML);
}).catch(e => console.log(e));
}
}); })();

JavaScript FileReader is duplicating the onload callback

The following code is driving me nuts. It picks up when a file upload input has changed and then grabs the image via FileReader. The annoying thing is, and i can't work out why, that is keeps incrementally duplicating the onload event. So the first time i select a file it fires onload once, if i select a second file with the same file input the onload fires twice, if i select a file again it fires 3 times and on like that.
var ele = document.getElementById('photo-upload');
ele.addEventListener('change',function(e){
console.log("FLE CHANGED");
var file = e.target.files[0];
var fr = new FileReader();
fr.onload = function(e){
console.log("FILE READER LOADED");
}
}
You are creating new file reader with each click on <input type="file" id="photo-upload" />.
I've modified your code:
const ele = document.getElementById('photo-upload');
const fr = new FileReader();
fr.onload = function(e){
console.log("FILE READER LOADED");
}
ele.addEventListener('change',function(e){
console.log("FLE CHANGED");
const file = e.target.files[0];
// load file with using on of fr methods
// eg.
fr.readAsArrayBuffer(file);
}
Working example:
const ele = document.getElementById('photo-upload');
const fr = new FileReader();
fr.onload = evt => {
console.log(evt.target);
console.log("FILE READER LOADED");
}
ele.addEventListener('change', evt => {
console.log("FLE CHANGED");
const file = evt.target.files[0];
fr.readAsArrayBuffer(file);
})
<input type="file" id="photo-upload" />

file content in textarea using Javascript

I want to display the content of a local file in a textarea-tag using javascript.
To do so, i found the following workaround:
<textarea id="queryContent"></textarea>
<input type="file" multiple id="queryInput">
<script>
var input = document.getElementById("queryInput");
input.addEventListener("change", function () {
Array.prototype.forEach.call(input.files, function (file) {
var reader = new FileReader();
reader.addEventListener("load", function () {
console.log("File", file.name, "starts with",
reader.result.slice(0,20));
});
reader.readAsText(file);
document.getElementById("queryContent").innerText = reader.result.toString();
});
});
</script>
The problem is i am not a pro in Javascript yet. i always get a reader.result is null error and i dont know why. I appreciate your help!
This line:
document.getElementById("queryContent").innerText = reader.result.toString();
should happen inside the callback. When the script runs this line, the FileReader has not finished his job yet and therefore reader.result is very likely to be null.
Please try this code:
var input = document.getElementById("queryInput");
input.addEventListener("change", function () {
Array.prototype.forEach.call(input.files, function (file) {
var reader = new FileReader();
reader.addEventListener("load", function () {
console.log("File", file.name, "starts with", reader.result.slice(0,20));
document.getElementById("queryContent").innerText = reader.result.toString();
});
reader.readAsText(file);
});
});
P.S.
I would recommend to remove the multiple from the input element, unless it is required, to avoid unnecessary complexity.

how to call function after two different event complete

I need to initialize cropper plugin in modal pop up. Whenever user click on image uploader I want to show that image in popup and it should initialize cropper plugin when modal pop up finishes its show animation as well as after image loaded completely.
Currently what is happening sometime initCroping function get called before image loaded and sometime it calls properly.
I want to call initCroping function after image loaded and after changing $("#crop-img") src, Finally it should check if modal pop up loaded completely then it should fire iniCroping function.
both events are unpredictable sometime modal pop up comes first sometimes image loads. I want to check both the event complete and then initCroping should call.
Is there any easy way to call function after these two events complete.
$('#cropModel').on('shown.bs.modal', function() {
//initCroping();
});
$(".upload").change(function(e){
var preview = $('#crop-img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener("load", function () {
//preview.src = reader.result;
$(preview).attr("src",reader.result);
initCroping();
}, false);
if (file) {
reader.readAsDataURL(file);
}
});
simpliest method:
var counter = 2;
function fireInitCroping() {
--counter === 0 && initCroping();
}
$('#cropModel').on('shown.bs.modal', function() {
fireInitCroping();
});
$(".upload").change(function(e){
var preview = $('#crop-img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener("load", function () {
//preview.src = reader.result;
$(preview).attr("src",reader.result);
fireInitCroping();
}, false);
if (file) {
reader.readAsDataURL(file);
}
});
With Screw-FileReader and a few promises works too
var shownPopup = new Promise(resolve =>
$('#cropModel').one('shown.bs.modal', () => resolve())
)
var loadedImage = new Promise((resolve, reject) => {
$(".upload").change(e => {
// create a new Image obj from Blob that resolves/reject onload or onerror
e.target.files[0].image().then(img => {
img.id = "crop-img"
$("#crop-img").replaceWith(img)
resolve()
} err => {
console.error('not an image')
reject(err)
})
})
})
// When both event's has fired then fire initCroping
Promise.all([shownPopup, loadedImage]).then(initCroping)

image preview before upload not working with + image+_row

When I select a image it should preview a image. But when I add my var image_row to onchnage it does not work.
I am trying to make it work with my onclick function function add_popup_image()
Codepen Example Here
Working single id
$("#fileupload_extra_image").change(function(){
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#input-popup-image').attr('src', e.target.result);
}
reader.readAsDataURL(this.files[0]);
}
});
Not Working
$('#fileupload_extra_image' + image_row).change(function(){
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#input-popup-image' + image_row).attr('src', e.target.result);
}
reader.readAsDataURL(this.files[0]);
}
});
Question: How Can I Make The + image_row work with my image preview script
The below was your problem:
image_row used to return always +1 i.e. if there existed
input-popup-image1 then it retrieved input-popup-image2. For time
being I just negated the value before searching for the id. You just
need to take care of the increment of image_row or the below code would just work fine.
Pen Here
$('#fileupload_extra_image' + image_row).on('change',function(){
if (this.files && this.files[0]) {
var reader = new FileReader();
var imgrw=image_row-1;
reader.onload = function (e) {
$('#input-popup-image' + imgrw).attr('src', e.target.result);
}
reader.readAsDataURL(this.files[0]);
}
});
UPDATE
To the problem you mentioned in your comments I would suggest to choose the below approach:
Updated Pen
Add a classname for the dynamically added controls image_preview and browse and then obtain its preview content which will be inside its root parent .row. So, this will avoid obtaining with id and keeping track of image_row value:
$(document).on('change','.file',function(){
if (this.files && this.files[0]) {
var imgpr=$(this).parents('div.row').find('.imgpr')
var reader = new FileReader();
reader.onload = function (e) {
$(imgpr).attr('src', e.target.result);
}
reader.readAsDataURL(this.files[0]);
}
});
$('#fileupload_extra_image' + image_row).change(function(){
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#input-popup-image' + image_row).attr('src', e.target.result);
image_row++;
}
reader.readAsDataURL(this.files[0]);
}
});
The problem is that you update image_row before the callback function for reader actually runs. What's happening is that you add an event listening on fileupload_extra_image + image_row for a change event which is fine. Then, it looks like you do a check for files and you do another event listener for reader on load. Note that this doesn't actually run this line of code yet:
$('#input-popup-image' + image_row).attr('src', e.target.result);
It's just simply saying that when reader is done loading, then run it.
Your function then continues and updates image_row which cause the previous line to use a value of 2 instead.
What my fix does is updates image_row only after a successful load is done.

Categories

Resources