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();
I am selecting multiple images using html tag 'input file multiple html' as below.
#Html.TextBoxFor(model => model.files, "",
new {#id = "filesToUploadID", #type = "file", #multiple = "multiple" })
<div class="col-md-10" id="selectedFiles"></div>
Then in javascript, I attached 'onchange' event listner to the above tag. When I select multiple images, I get all the attached images to the tag using jquery. But I need image file names as well. While getting image file names, I get only one file name poplulated in my html along with image using jquery.
jquery/ javascript is below
document.addEventListener("DOMContentLoaded", init, false);
function init() {
document.querySelector('#filesToUploadID').addEventListener('change', handleFileSelect, false);
selDiv = document.querySelector("#selectedFiles");
}
function handleFileSelect(e) {
debugger;
NameArray = [];
if (!e.target.files) return;
selDiv.innerHTML = "";
var files = e.target.files;
for (var i = 0; i < files.length; i++) {
var f = files[i];
NameArray.push(f.name);
var reader = new FileReader();
reader.onload = function (e) {
var html = "<img src='" + e.target.result + "' />";// + "<div>" + + "</div>";//+ "<br clear=\"left\"/>";
$(selDiv).append($(html));
//selDiv.innerHTML += html;
}
reader.readAsDataURL(f);
}
}
I tool help from this link and implemented both ways but could not succeed.
All images names are relevant to the images. What I am getting is like this
It's source code is
I have to assign id's based on image names that's why it is important to get relevant image names. Any lead to the topic will be high appreciated.
Thank you.
var f = files[i];
var fileName = e.target.f.name
Is it possible to add metadata information along with html 5 images before they are uploaded?
clarification: html 5 images upload (like the drag and drop thumbnail method or single input file) sample HTML 5 Upload/drag_drop
So say user is uploading 10 images, he will fill some text info for each image describing it, then this information will be saved on the server.
So for each thumbnail user can add "title","location","description" in text boxes.
the example below allows an upload of a single image and preview of a thumbnail, what I am trying to achieve is to interact with this thumbnail and submit some text to the server along with it.
clarification 2: this question is not related to the picture EXIF,
I am asking about general metadata information user can add to the picture, like "I took the picture right before my wedding", or "this picture of my old home" or like tags "Red"
document.getElementById("files").onchange = function () {
var reader = new FileReader();
reader.onload = function (e) {
// get loaded data and render thumbnail.
document.getElementById("image").src = e.target.result;
};
// read the image file as a data URL.
reader.readAsDataURL(this.files[0]);
};
<input type="file" id="files" />
<img width="100" height="140" id="image" />
I am not sure why you are getting down votes for this question. It may have something to do with your wording. I assume you mean Exif data rather then metadata. See https://en.wikipedia.org/wiki/Exif
For modifying Exif data of an image in JavaScript you can use piexifjs by hMatoba, it will allow you to modify the images Exif metadata before uploading to the server.
Hope this helps )
Maybe this is what you need.
As charlietfl already said in the comments you can use FormData
Here is an example:
First try with multiple inputs
Here you can see how it is saved than:
{
"args": {},
"data": "",
"files": {
"input1": "yourimg1",
"input2": "yourimg2"
},
"form": {
"input1": [
"house",
"london"
],
"input2": [
"bridge",
"berlin"
]
},
"headers": {
....
}
Now you have your files and descriptions connected with the name of the input field. It is important that all the beloning inputfields have the same name.
So the file from input1 belongs to the description in the array named input1 and the file from input2 to the array input2 and so on.
A few good explanations you can find here and if you want you can use this site to test the code and output.
You can take the code from here. Add at the end of the js function
console.log(request.response);
and change the url to http://httpbin.org/post.
Or just play a little bit around with it.
EDIT:
If you want it with multiple files you can do it like this:
var testForm = document.getElementById('test-form');
var div = document.getElementById('output');
var data;
testForm.onsubmit = function(event) {
event.preventDefault();
var request = new XMLHttpRequest();
request.open('POST', 'http://your.url/post', /* async = */ false);
var formData = new FormData(document.getElementById('test-form'));
request.send(formData);
}
function handleFileSelect(evt) {
var files = evt.target.files;
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML =
[
'<img src="',
e.target.result,
'" title="',
escape(theFile.name),
'"/><div class="desc"><div>Location:<input name="',
escape(theFile.name),
'"> </div><br><div>Title:<input name="',
escape(theFile.name),
'"></div><br><div>Description:<input name="',
escape(theFile.name),
'"></div><br></div>'
].join('');
document.getElementById('list').insertBefore(span, null);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
//makes description visible
$(document).on("click","img",function(){
$(this).next().css("display", "block");
});
document.getElementById('files').addEventListener('change', handleFileSelect, false);
.desc{
display:none;
}
img{
height: 75px;
border: 1px solid #000;
margin: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id='test-form'>
<input type="file" id="files" name='input1' multiple/><br>
<output id="list"></output><br>
<input type='submit'>
</form>
Then this is you output:
{
"args": {},
"data": "",
"files": {
"input1": "all_your_images"
},
"form": {
"favicon.png": [
"1a",
"1b",
"1c"
],
"loginBackground.jpg": [
"2a",
"2b",
"2c"
]
},
"headers": {
....}
The disadvantage is that all images are uploaded together.
As you can see now each description is safed in an array with the name of its beloning image.
<div>Location:<input name="', escape(theFile.name),'"> </div>
This gives the inputname the name of the belonging image.
So if you want to safe the images and descriptions on your server in an array or something like that you have to get the names of the uploaded images with PHP.
$filename = pathinfo($_FILES['picture']['name'], PATHINFO_FILENAME);
(see HERE)
I know you want it with drag and drop, but the code would be to long with that.
If you still want to do this I can recomment you this site.
You only have to add the functionality to my posted code and it should work.
If you want to safe the informations directly in the image then I only can point to the answer of tnt-rox. Then you should visit his links and read more about exif.
I think the problem is that you put static image element. So I make a code that generates images and title text box for every file and this is the code snippet for what I reach
var count = 0;
document.getElementById("files").onchange = function (e) {
var image = document.createElement("img");
image.width = "100";
for(var i=0;i<this.files.length;i++){
var reader = new FileReader();
reader.onload = function (e) {
//Add the image
var im = image.cloneNode();
im.id= "image"+count;
im.src = e.target.result;
document.body.appendChild(im);
//Add the title for example
var title = document.createElement("input");
title.id = "title-img"+count;
title.placeholder= "Enter the title of image "+count;
document.body.appendChild(title);
count++;
};
reader.readAsDataURL(this.files[i]);
}
};
<html>
<head>
</head>
<body>
<input type="file" multiple id="files" />
</body>
</html>
I hope it helps you.
For this Purpose, Just use the Form to get the details from the user. The code example is given below,
HTML Code,
<Form action="imageSave.php" method="Post">
<table>
<tr>
<td> Title </td>
<td> <input type="text" name="imageTitle" > </td>
<tr>
<tr>
<td> Location</td>
<td> <input type="text" name="imageLocation" > </td>
<tr>
<tr>
<td> Description</td>
<td> <textarea name="description" > </textarea> </td>
<tr>
<tr>
<td> Image </td>
<td> <input type="file" name="files" > </td>
<tr>
</table>
</Form>
In your Database add these Columns,
TableName : tblImages
Column 1 : id [int]
Column 2 : imageTitle [varchar(255)]
Column 3 : imageLocation [varchar(255)]
Column 4 : description [text]
Column 5 : imageName [varchar(255)]
Then use this PHP File (imageSave.php)
$imageTitle=$_POST['imageTitle'];
$imageLocation=$_POST['imageLocation'];
$description=$_POST['description'];
$imageName=$_FILES['files']['name'];
if ($_FILES["files"]["error"] == 0)
{
$remove_these = array(' ','`','"','\'','\\','/','(',')');
$newname = str_replace($remove_these,'',$_FILES['files']['name']);
//Make the filename unique
$newname = time().".".$extension;
//Save the uploaded the file to another location
$upload_path = "/images/$newname";
//echo $upload_path;
move_uploaded_file($_FILES['files']['tmp_name'], $upload_path);
// Query and Code to Update the Database
}
I have a form that allows users to upload multiple images. Users has an option to remove Images one by one if they don't want to upload that particular image.
Now, How to remove the value from the file type of the one that they removed (e.g. didn't want to upload)?
I have tried using ($('input').val("")) but it works only for single image not for multiple images. If I used this for multiple image then all uploaded images value become empty instead of that particular image that user want to remove.. Below is my Code:-
HTML
<input type="file" id="files2" name="cert_img" multiple>
<output id="list2"></output>
Javascript
var count2=0;
function handleFileSelect2(evt) {
var $fileUpload = $("input#files2[type='file']");
count2=count2+parseInt($fileUpload.get(0).files.length);
var files = evt.target.files;
for (var i = 0, f; f = files[i]; i++) {
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
reader.onload = (function (theFile) {
return function (e) {
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" id="image_X" src="', e.target.result, '" title="', escape(theFile.name), '"/><span class="remove_img_preview"></span>'].join('');
document.getElementById('list2').insertBefore(span, null);
};
})(f);
reader.readAsDataURL(f);
}
}
$('#files2').change(function(evt){
handleFileSelect2(evt);
});
$('#list2').on('click', '.remove_img_preview',function () {
$(this).parent('span').remove();
//$('input').val("");
});
Thanks in advance..
According to this question you can't change FileList content because it is readonly.
But if you want to remove a file of FileList, you can create a new object and set to it files that you want to upload. Then use created object to upload files. Run my example and select multiple files. Then click on any file that you want to delete. After delete files, see browser console.
var files;
$("#files").on("change", function(e){
files = $("#files")[0].files;
$.each(files, function(index, value){
$("ol").append("<li data-index='"+index+"'>"+ value.name +"</li>");
});
});
$(document).on("click", "li", function(){
var newFiles = {};
var index = $(this).index();
$("li:nth-child(" + (index + 1) + ")").remove();
$("li").each(function(index, element){
var dataIndex = $(element).attr("data-index");
newFiles[index] = files[dataIndex];
});
console.log(newFiles);
});
li:hover {
color: red;
cursor: default;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="file" id="files" multiple>
<ol></ol>
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">