I am kind of new to Angular and right now I am trying to select multiple files and display their preview before uploading them to server.
My below code works fine if there are multiple files but selected separately, hovewer while trying to select multiple images at once, only the last one is rendered. I checked with console.log and while selecting multiple files at once the reader.result of the previous files except the last one is always null ever though the reader.onload is executing (the object is also inserted into the array, but without the image url).
I want to be able to also remove the selected files, so both files and their imagesUrl are kept in an FileWithImage object to keep the right order of their previews.
component.ts:
onFilesSelected(event: any): void {
this.selectedFiles = event.target.files;
for (let i = 0; i < this.selectedFiles.length; i++) {
let fileWithImage: FileWithImage = {
imageUrl: '',
file: this.selectedFiles[i],
};
var reader = new FileReader();
reader.readAsDataURL(fileWithImage.file);
reader.onload = (event) => {
fileWithImage.imageUrl = reader.result;
console.log(fileWithImage.file.name);
console.log(fileWithImage.imageUrl);
this.filesWithImages.push(fileWithImage);
};
}
}
component.html (selecting and displaying images):
<button
type="button"
mat-raised-button
(click)="fileInput.click()"
class="button"
>
Choose File
</button>
<input
hidden
(change)="onFilesSelected($event)"
#fileInput
type="file"
accept="image/*"
multiple="multiple"
/>
<div *ngFor="let file of filesWithImages" class="images">
<img
[src]="file.imageUrl"
height="10%"
width="10%"
*ngIf="file.imageUrl"
/>
</div>
I tried blocking the loop untill the reader.onload is executed with a use of a flag, but it did not work.
I also tried to get the url directly from a method:
createImageUrl(file: File): any {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (event) => {
return reader.result;
};
}
and call it in [src] of an image tag in html, but it made my program crash.
The main problem is probably why the result of reader is null for the first few files and only the last one is properly displayed. Can someone help me to find the cause of this problem?
Try changing reader variable to local scope.
Change
var reader = new FileReader();
to
let reader = new FileReader();
Related
I would like to seek some help please regarding loading txt file content to variable using input element in javascript.
So i am working with java script file separate from html file, and when the user click on button to load the file from the html side as below:
<input type="file" id="selectedFile" accept=".txt" />
it fires the onchange event as below
document.getElementById('selectedFile').addEventListener('change', function () {
var fr = new FileReader();
fr.onload = function () {
document.getElementById('display').textContent = fr.result;
console.log(fr.result);
}
fr.readAsText(this.files[0]);
})
basically if i console log the fr.result as in the code up there, i can see the content with no issues, and also i have div element with id "display" and content gets updated no issues. however i am unable to get the fr.result to be stored into global variable and use it later on in my code.
I tried the below code:
let test = "";
document.getElementById('selectedFile').addEventListener('change', function () {
var fr = new FileReader();
fr.onload = function () {
document.getElementById('display').textContent = fr.result;
test = fr.result;
}
fr.readAsText(this.files[0]);
})
console.log(test);
but the test variable in console comes out empty and runs even before i choose the file with input element.
Any advices about what i missing here, and how i can get the file content using same method to be stored in variable?
Thanks!
I tried it it works like this, it was maybe because your event listener function was an anonymous function, and you can't call this in anonymous function
<input type="file" id="selectedFile">
<p id="display"></p>
<script>
var fr = new FileReader();
let test;
document.getElementById('selectedFile').addEventListener('change', x);
function x() {
fr.onload = ()=>{
document.getElementById('display').innerText = fr.result;
console.log(fr.result);
test = fr.result;
}
fr.readAsText(this.files[0]);
}
console.log(test);</script>
Id like to implement a UI where the user selects an image and that image is instantly displayed back to them for review. The user would have to click "submit" to upload/save the image to their profile.
I am having issues with the "instantly display back to the user part".
I am using angular FormData with the following markup & controller:
MARKUP
<input id="chooseFile" type="file" file-model="picFile" />
<img src="{{uploadedImage}}" /> <!-- this populates with filename but what is the path?? -->
CONTROLLER
angular.element('#chooseFile').change(function(){
var file = $scope.picFile; // this comes up "undefined" since file is still uploading when this is fired
$scope.uploadedImage = file.name;
});
I have 2 primary issues with the above code (described in comments):
1) In the controller, file comes up undefined obviously because even the smallest file takes >0s to upload while the callback is fired pretty much instantaneously. I got it work using $timeout but thats a bit of a lame hack. How can I have the callback wait until the file is uploaded??
2) The idea is to upload the file and display it in the img tag using Angular's data-binding. This works in that src is populated with the filename, but what is the path of the img. Some temporary location in cache or something?? Obviously I havent set a path to move the file yet.
Any help appreciated!
I also needed this feature, some how I manage to display image instantly.
angular.module('HelloWorldApp', [])
.controller('HelloWorldController', function($scope) {
$scope.uploadavtar = function(files) {
//var fd = new FormData();
//Take the first selected file
//fd.append("file", files[0]);
var imagefile = document.querySelector('#file');
if (imagefile.files && imagefile.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#temp_image')
.attr('src', e.target.result);
};
reader.readAsDataURL(imagefile.files[0]);
this.imagefile = imagefile.files[0];
}else{
console.log("Image not selected");
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="HelloWorldApp">
<div ng-controller="HelloWorldController">
<input type="file" id="file" onchange="angular.element(this).scope().uploadavtar(this.files)"/>
</div>
<img src="" id="temp_image" width="100">
<div>
</div>
</div>
I was using laravel + Angularjs so another related post to store image is : https://stackoverflow.com/a/34830307/2815635
I want to insert an image that a user selects from their local machine using FileReader.readAsDataURL() into multiple <img> elements on a single html page.
Using the example code provided by the MDN docs for FileReader.readAsDataURL(), the image only gets inserted into the first img element, not the rest.
I thought the reason the image is only inserted into the first <img> instance is because the example code uses document.querySelector('img'). However, when I use document.querySelectorAll('img'), it still does not work.
The MDN docs provide a working codepen example that you can see in action. Here is their static code:
<!--html-->
<input type="file" onchange="previewFile()"><br>
<img src="" height="200" alt="Image preview...">
and
//js
function previewFile() {
var preview = document.querySelector('img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener("load", function () {
preview.src = reader.result;
}, false);
if (file) {
reader.readAsDataURL(file);
}
}
Here is a working codepen of my code, and below is my static code.
<div>
<label>Select an Image</label>
<input type="file" onchange="previewFile()">
</div>
<div>
<img id="small" src="http://placehold.it/900x900"/>
<img id="med" src="http://placehold.it/1200x1200"/>
<img id="large" src="http://placehold.it/1500x1500"/>
</div>
<script>
function previewFile() {
var preview = document.querySelector('img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener("load", function () {
preview.src = reader.result;
}, false);
if (file) {
reader.readAsDataURL(file);
}
}
</script>
Can anyone offer some help to get the user selected image to populate all of the <img> elements on the page instead of just the first element?
Not quite sure to correctly understand your need.
If it's like this:
you expect only one file (image) to be selected through your <input>
and you want this unique image to be the same source of all your <img>s
Then below is the way to go:
function previewFile() {
var preview = document.querySelectorAll('img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener("load", function () {
for (var img in preview) {
preview[img].src = reader.result;
}
}, false);
if (file) {
reader.readAsDataURL(file);
}
}
<div>
<label>Select an Image</label>
<input type="file" onchange="previewFile()">
</div>
<div>
<img id="small" src="http://placehold.it/100x100"/>
<img id="med" src="http://placehold.it/200x200"/>
<img id="large" src="http://placehold.it/300x300"/>
</div>
The main points are:
preview gets all <img>s (as you already tried)
then we use a for() loop to process each of them (here was your lack)
The code will need to iterate over all the files in the file list and then assign each (via Object-URL) to separate image element.
I would suggest the following changes as there is really no need for FileReader at this point. The URL.createObjectURL() can be used with File blobs as well and save us from some headache:
document.querySelector("input[type=file]").onchange = previewFile;
function previewFile() {
var files = this.files; // "this" = input element
var parent = document.querySelector(".imageList"); // parent element
var i = 0, file;
while(file = files[i++]) { // iterate over file list
var img = new Image(); // create new image instance
img.src = (URL || webkitURL).createObjectURL(file); // use File blob directly
parent.appendChild(img); // insert image in DOM
};
}
<div>
<label>Select an Image
<input type="file" multiple> <!-- make sure multiple is enabled -->
</label>
</div>
<div class="imageList"></div>
If you want to replace an image list simply obtain the list of images using querySelectorAll() instead of querySelector(), then replace the sources from the list until one run dry:
//... as before
var img, images = document.querySelectorAll("img");
while((file = files[i]) && (img = images[i++])) { // loop until one runs dry
img.src = (URL || webkitURL).createObjectURL(file); // use File blob directly
};
document.querySelector("input[type=file]").onchange = previewFile;
function previewFile() {
var files = this.files; // "this" = input element
var parent = document.querySelector(".imageList"); // parent element
var i = 0, file;
var img, images = document.querySelectorAll("img");
while((file = files[i]) && (img = images[i++])) { // loop until one runs dry
img.src = (URL || webkitURL).createObjectURL(file); // use File blob directly
};
}
<div>
<label>Select an Image
<input type="file" multiple> <!-- make sure multiple is enabled -->
</label>
</div>
<div class="imageList">
<img id="small" src="http://placehold.it/100x100"/>
<img id="med" src="http://placehold.it/200x200"/>
<img id="large" src="http://placehold.it/300x300"/>
</div>
I am trying to implement a JavaScript program to read a bunch of CSV files from a particular directory containing sub-directories. To do so, I am thinking of using Javascript to read multiple files using webkitdirectory and reading the files and then using java to push them.
HTML Form
<input id="csv" type="file" multiple webkitdirectory directory>
<input type="button" onclick="readCSV()" value="Submit">
JavaScript to Read Files
function readCSV(){
var fileInput = document.getElementById("csv");
var fileCount = fileInput.files.length;
if( fileCount != 0) {
alert(fileCount);
var reader;
for (var i = 0; i < fileCount; i++) {
reader = new FileReader();
reader.onload = function () {
document.getElementById('out').innerHTML = reader.result;
// Once the file is read, Send it to JavaServlet to save on server.
};
reader.readAsBinaryString(fileInput.files[i]);
}
} else {
alert("Select a File");
}
}
The problem I am facing here is that the code runs successfully when only one file is selected but breaks when multiple files are selected. I think the problem is in sync read. But I couldn't find a way to either sync read or pause the loop while the reader object completely reads the file.
For context, I'm trying to create a "click image" file uploader. Initially there is a default image, which I then click. I trigger a file upload, and the user picks an image file they want. Then I will set the image to replace the default (and do other things with it later). Right now, the front end looks something like this:
<div class="right-preview">
<input type="image" src="img/logo.png" height="240px" width="240px" ng-click="uploadImage('right-image')" id="upload-right-image"/>
<input type="file" id="upload-right" style="visibility: hidden">
</div>
When the image is clicked, it triggers an upload action.
$scope.uploadImage = function(side) {
$image = $('#upload-' + side);
$fileInput = $('#upload-right');
$fileInput.change(function(changeEvent) {
var files = changeEvent.target.files;
for(var i = 0; i < files.length; i++) {
file = files[i];
console.log(file);
}
});
$fileInput.trigger('click');
}
When the change event is fired after the user finishes picking their file, I have the changeEvent and I know they've selected their file. Each of the files has some properties (like name and size) but I'm not seeing anything for accessing the raw data so I can set the src on my other element.
Am I just completely missing how to get the image data, or is there a better way to do this? I have no server (right now) to post this to. Perhaps there is a better way to approach this?
This link may be helpful to you - https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
I took one method from that page and added some additional functionality to hide the file upload button and have the image placeholder trigger its click event.
$('#placeholder').click(function() {
$('#img-upload').trigger('click');
});
function previewFile() {
var preview = document.querySelector('img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener("load", function () {
preview.src = reader.result;
}, false);
if (file) {
reader.readAsDataURL(file);
}
}
.hidden {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<img width="250" height="250" id="placeholder" src="http://place-hold.it/250x250&text='click to upload'">
<input class="hidden" type="file" onchange="previewFile()" id="img-upload">