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.
Related
You can see my demo here.
I have simple html:
<input type="file" id="files" multiple>
<div class="preview"></div>
So when user choose image he can preview it, and when he click on upload button and add another images he also will see in preview that this images was added.
My problem that I want to add for each appended div a data attribute with index value of it.
My js:
$("#files").on("change", previewFiles);
function previewFiles() {
var preview = $('.preview');
var files = $(this)[0].files;
function readAndPreview(file) {
if ( /\.(jpe?g|png|gif)$/i.test(file.name) ) {
var reader = new FileReader();
reader.onload = function(event) {
var conta = $('<div></div>').addClass("preview__image");
var img = $('<img>');
img.attr('src', event.target.result);
img.attr('title', file.name);
img.appendTo(conta);
conta.appendTo( preview );
}
reader.readAsDataURL(file);
}
}
if (files) {
[].forEach.call(files, readAndPreview);
}
var child = preview.find(".preview__image");
child.each(function(index) {
$(this).attr("data-index", index);
});
}
In my code there is a problem, when user choose image for the first time, data attribute is not created, if he upload image for the second time, appended before div or divs which was first will get data attribute, but new appended div or divs not.
What am I doing wrong?
P.S. I know that my input is clearing its value before uploading another images. All I want is related to preview block. Thanks in advance.
This is happening because you're trying to talk to an element that is not yet in the DOM - because onload is an asynchronous event.
In other words, the element with .preview__image does not get added until AFTER you're running preview.find('.preview__image').
There's various ways round this. One would be to use Promise.all() and convert your reader function to return a promise that gets resolved only once the onload callback has completed.
We could attach a then() to Promise.all, but more elegant would be to use await, which means we'll need to make your outer function asynchronous, by prefixing async to it.
All in all (I've commented the key changes):
$("#files").on("change", previewFiles);
async function previewFiles() { //<-- prefix with async
var preview = $('.preview');
var files = $(this)[0].files;
function readAndPreview(file) {
return new Promise(res => { //<-- reader func now returns promise...
if ( /\.(jpe?g|png|gif)$/i.test(file.name) ) {
var reader = new FileReader();
reader.onload = function(event) {
var conta = $('<div></div>').addClass("preview__image");
var img = $('<img>');
img.attr('src', event.target.result);
img.attr('title', file.name);
img.appendTo(conta);
conta.appendTo( preview );
res(); //...which is resolved once onload is complete
}
reader.readAsDataURL(file);
}
});
}
//now let's have a master promise that waits for the sub-promises to resolve
await Promise.all([...files].map(file => readAndPreview(file)));
//now we can talk to the updated DOM
var child = preview.find(".preview__image");
child.each(function(index) {
$(this).attr("data-index", index);
});
}
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));
}
}); })();
I need to read a file locally using javascript.
I know that for security reasons the possibility of doing this directly is inhibited.
Is there any way to work around this?
Furthermore, if I try to use the features of the File API specification, I can't get the "load" button to work if the button itself is not directly attached in the document.body. I would like this button to work even if attached to child type elements. Is there a solution for this?
Thank you very much
fileReader() {
console.log("FileReader Called");
var fileInput = document.getElementById('file');
var fileDisplayArea = document.getElementById('source-container');
fileInput.addEventListener('change', function(e) {
var file = fileInput.files[0];
var textType = /text.*/;
if (file.type.match(textType)) {
var reader = new FileReader();
reader.onload = function(e) {
fileDisplayArea.innerText = reader.result;
}
reader.readAsText(file);
} else {
fileDisplayArea.innerText = "File not supported!";
}
});
}
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" />
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.