How to read multiple file inputs using FileReader API in js - javascript

I was looking for a solution though there are many answers on the same topics I was unable to figure out the problem in my code.
The problem is, I can only read the first file input using this API.
The last two inputs show undefined if I console.log(e.target.files[1]);
I am using vue 2. Here is the codes I have.
For the three inputs I have
<input type="file" name="file[]" #change="img1">
<input type="file" name="file[]" #change="img2">
<input type="file" name="file[]" #change="img3">
img1(e){
console.log(e.target.files[0]);
this.readFile(e,'img1',0);
},
img2(e){
console.log(e.target.files[1]);
this.readFile(e,'img2',1);
},
img3(e){
console.log(e.target.files[2]);
this.readFile(e,'img3',2);
},
Here is my readFile method
readFile(e,img,i) {
let self=this;
if(window.FileReader) {
var file = e.target.files[i];
var reader = new FileReader();
if (file) {
let type=e.target.files[i].type;
console.log(type);
if(!this.cheackType(type)){
this.showTypeWarn('Invalid Image Formate', 'Please Upload jpg or a png file only');
return
}
reader.readAsDataURL(file);
} else {
// img.css('display', 'none');
// img.attr('src', '');
}
reader.onloadend = function (e) {
self.data.img=reader.result;
}
}
}
Thank you.

When you do console.log(e.target.files[i]); you are accessing the i-th file of the element that fired the event. You should try with console.log(e.target.files[0]); to access the first file of each input.

Related

Is there an event listener for on confirm files to upload? [duplicate]

Is there any chance to detect every file selection the user made for an HTML input of type file element?
This was asked many times before, but the usually proposed onchange event doesn't fire if the user select the same file again.
Set the value of the input to null on each onclick event. This will reset the input's value and trigger the onchange event even if the same path is selected.
var input = document.getElementsByTagName('input')[0];
input.onclick = function () {
this.value = null;
};
input.onchange = function () {
console.log(this.value);
};
<input type="file" value="C:\fakepath">
Note: It's normal if your file is prefixed with 'C:\fakepath'. That's a security feature preventing JavaScript from knowing the file's absolute path. The browser still knows it internally.
Use onClick event to clear value of target input, each time user clicks on field. This ensures that the onChange event will be triggered for the same file as well. Worked for me :)
onInputClick = (event) => {
event.target.value = ''
}
<input type="file" onChange={onFileChanged} onClick={onInputClick} />
Using TypeScript
onInputClick = ( event: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
const element = event.target as HTMLInputElement
element.value = ''
}
<form enctype='multipart/form-data'>
<input onchange="alert(this.value); this.value=null; return false;" type='file'>
<br>
<input type='submit' value='Upload'>
</form>
this.value=null; is only necessary for Chrome, Firefox will work fine just with return false;
Here is a FIDDLE
In this article, under the title "Using form input for selecting"
http://www.html5rocks.com/en/tutorials/file/dndfiles/
<input type="file" id="files" name="files[]" multiple />
<script>
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
// files is a FileList of File objects. List some properties.
var output = [];
for (var i = 0, f; f = files[i]; i++) {
// Code to execute for every file selected
}
// Code to execute after that
}
document.getElementById('files').addEventListener('change',
handleFileSelect,
false);
</script>
It adds an event listener to 'change', but I tested it and it triggers even if you choose the same file and not if you cancel.
handleChange({target}) {
const files = target.files
target.value = ''
}
<input #myInput type="file" id="imgFile" (click)="$event.target.value=null"
(change)="detectUploadedImage($event)" accept="image/*" />
Clearing the value of 0th index of input worked for me. Please try the below code, hope this will work (AngularJs).
scope.onClick = function() {
input[0].value = "";
input.click();
};
Usage of two way binding worked for me if you are working with Angular.
Here is my HMTL
<input type="file" #upload name="upload" [(ngModel)]="inputValue"(change)='fileEvent($event)'/>
and TS..
#ViewChild('upload') uploadBtn: HTMLElement;
fileEvent(e: any){
//file upload part...
this.inputValue = "";
}
The selected answer (using state to set input value null) gave me an error.
I use empty strings instead
const [fileValue, setFileValue] = React.useState("")
<input
onClick={() => {
setFileValue("");
}}
type="file"
value={fileValue}
onChange={handleAddFile}
/>
Do whatever you want to do after the file loads successfully.just after the completion of your file processing set the value of file control to blank string.so the .change() will always be called even the file name changes or not. like for example you can do this thing and worked for me like charm
$('#myFile').change(function () {
LoadFile("myFile");//function to do processing of file.
$('#myFile').val('');// set the value to empty of myfile control.
});

How to remove certain file from multiple input type="file"?

I've got an issue with my uploads,
I'm using FileReader with readAsDataURL() method, when user select its images then he can preview files.
Also I have a button which has classname "del-first", when user click on it first image should be deleted from preview and also from input value. That's my problem.
So I have an input:
<form method="post" enctype="multipart/form-data">
<input type="file" id="myFiles" name="myFiles[]" accept="image/png, image/jpeg" multiple />
<div class="preview-wrapper">
<button class="del-first"></button>
</div>
<input type="submit" value="SUBMIT" />
</form>
My Javscript:
const fileInput = document.getElementById("myFiles");
function previewFiles() {
var preview = document.querySelector('.preview-wrapper');
var files = fileInput.files;
function readAndPreview(file) {
// Make sure `file.name` matches our extensions criteria
if ( /\.(jpe?g|png|gif)$/i.test(file.name) ) {
var reader = new FileReader();
reader.addEventListener("load", function() {
var conta = document.createElement('div');
conta.className = "preview-image";
var image = new Image();
image.height = 100;
image.title = file.name;
image.src = this.result;
conta.appendChild( image );
preview.appendChild( conta );
}, false);
reader.readAsDataURL(file);
}
}
if (files) {
[].forEach.call(files, readAndPreview);
}
var newFileList = Array.from(event.target.files);
console.log(newFileList);
var imgRemove = document.querySelector('.del-first');
imgRemove.addEventListener("click", function(e) {
e.preventDefault();
newFileList.splice(0,1);
console.log(newFileList);
})
}
fileInput.addEventListener("change", previewFiles);
As you can see in the end of the script I used array.from for my files:
var newFileList = Array.from(event.target.files);
then I added listener, when user click button first object should be deleted:
var imgRemove = document.querySelector('.del-first');
imgRemove.addEventListener("click", function(e) {
e.preventDefault();
newFileList.splice(0,1);
console.log(newFileList);
})
So in my console.log I'm getting that it has been deleted, but now I need somehow push my newFileList to my input, so when user click submit I get on my server valid files. But I don't know how to make it?
According to my understanding, I don't think you could remove the file from your file list. The file's data in files is read-only, so I recommend you use multiple <input type="file"> for adding file instead of <input type="file" multiple>, so that you can easily remove the specific file with the simple remove button (clear the value of the file input). It may be inconvenient for the user, but it works in your case I think.
========Update
Instead of using <input type="file" multiple>. You can use the alternative way like this. You can take a look of this
Hope it would help. Please correct me if I were wrong

Append Files in Input Type File with Multiple Before Uploading

I have a form on my laravel website with a section that allows users to upload multiple image files (with a preview of the selected images). If a user only chooses multiple images once, everything works, and the selected images get uploaded. However, if a user selects multiple images on two separate occasions before submitting the form, only the last bunch of selected images will be uploaded.
For example; a user selects 4 images, then they fill out some other fields on the form, and then they decide to add 3 more images on top of the 4 images they previously selected before submitting the form. The image preview box will contain all 7 images they have selected, but once they submit the form, only the last 3 images they selected will be uploaded.
HTML Multiple File Input:
<input id="gphotos" type="file" name="gphotos[]" multiple>
<div class="preview"></div>
JavaScript:
function gphoto(input, ImagePreview) {
if (input.files) {
var fileAmount = input.files.length;
for (i = 0; i < fileAmount; i++) {
var reader = new FileReader();
reader.onload = function(e) {
$('.preview').css('margin-top', '4px');
$($.parseHTML('<img class="gphoto">')).attr('src', e.target.result).appendTo(ImagePreview);
}
reader.readAsDataURL(input.files[i]);
}
}
};
$('#gphotos').change(function() {
gphoto(this, 'div.preview');
});
Controller:
if (request('gphotos')) {
$imageArray['gphotos'] = array();
foreach (request()->gphotos as $gphoto) {
$gphotoPath = $gphoto->store('gphotos', 'public');
$imageArray['gphotos'][] = $gphotoPath;
}
$imageArray['gphotos'] = json_encode($imageArray['gphotos']);
}
I would like users to be able to select multiple images on multiple separate occasions before submitting the form, and all their images being uploaded. I have tried searching all over the internet, but no one has a simple solution to this seemingly simple problem. I would greatly appreciate it if someone could help me solve this issue.
Instead of letting the form handle it's own submission, create your own. Listen for the submit event on the form and whenever you submit, cancel the default behavior and send your data with AJAX. In this case the jQuery ajax function. Send it to a PHP file which handles your incoming request.
You can store files, like images, in a FormData object. It's a container to set key / value pairs to send with an HTTP request. So create one.
In your change event listener you'll want to add each selected image to this FormData container so that the images will be stored there. This way you can select new images every time and add them to the container. And when you submit you only submit that container with the images in them.
Then when images are uploaded successfully loop through the container and delete each key in it so the user can restart the whole process.
<form id="gphotos-form" enctype="multipart/form-data">
<input id="gphotos" type="file" name="gphotos[]" multiple>
<div class="preview"></div>
<button type="submit">Submit</button>
</form>
// Container for the images.
const selectedImages = new FormData();
function gphoto(input, imagePreview) {
for (const file of input.files) {
const image = new Image();
image.classList.add('gphoto');
image.src = URL.createObjectURL(file);
image.onload = event => {
$(imagePreview).append(image);
URL.revokeObjectURL(event.target.src);
};
// Image is added here.
selectedImages.append('gphotos', file);
}
}
$('#gphotos').on('change', function() {
gphoto(this, 'div.preview');
});
$('#gphotos-form').on('submit', function(event) {
// Get the data from the form, but remove the gphotos[] one,
// because we want to use our own collection of images.
const formData = new FormData(event.target);
formData.delete('gphotos[]');
for (const file of selectedImages.values()) {
formData.append('gphotos', file);
}
// Send container to your server.
// Change the url: value to your PHP script.
$.ajax({
url: 'http://example.com/script.php',
data: formData,
processData: false,
contentType: false,
type: 'POST',
success: function(data){
console.log(data);
// Delete all the images from the container.
for (let key of selectedImages.keys()) {
selectedImages.delete(key)
});
// Clear the preview.
$('div.preview').html('');
}
});
event.preventDefault();
});
You could add a new <input type="file"> whenever you finished uploading the previous files and hide the previous input. This way you keep adding a new input every time you want to add more files and prevent the previous input from being overwritten.
And it doesn't require you to use AJAX.
<form id="gphotos-form" enctype="multipart/form-data">
<div class="gphotos-upload">
<input class="gphotos" type="file" name="gphotos[]" multiple>
</div>
<div class="preview"></div>
<button type="submit">Submit</button>
</form>
.ghpotos[hidden] {
display: none;
}
let $uploads = $('.gphotos-upload');
let $preview = $('div.preview');
$('#gphotos-form').on('change', function(event) {
let $currentInput = $(event.target);
let $newInput = $('<input class="gphotos" type="file" name="gphotos[]" multiple>');
for (const file of event.target.files) {
const image = new Image();
image.classList.add('gphoto');
image.src = URL.createObjectURL(file);
image.onload = event => {
$preview.append(image);
URL.revokeObjectURL(event.target.src);
};
}
// Disabled and hide current input.
$currentInput.attr('hidden', '');
// Add new input to do another upload.
$uploads.append($newInput);
});
HTML Multiple File Input:
<input id="gphotos" type="file" name="gphotos[]" multiple>
<div class="store"></div>
<div class="preview"></div>
JavaScript:
function gphoto(input, ImagePreview) {
var files = input.files;
var filesArr = Array.prototype.slice.call(files);
filesArr.forEach(function(f) {
if (!f.type.match("image.*")) {
return;
}
var reader = new FileReader();
reader.onload = function(e) {
$('.preview').css('margin-top', '4px');
$($.parseHTML('<img class="gphoto">')).attr('src', e.target.result).appendTo(ImagePreview);
$($.parseHTML('<input type="hidden" name="photos[]">')).attr('value', e.target.result).appendTo($('.store'));
};
reader.readAsDataURL(f);
});
}
$('#gphotos').change(function() {
gphoto(this, 'div.preview');
});
Controller:
if (request('photos')) {
$imageArray['gphotos'] = array();
foreach (request()->photos as $gphoto) {
$base64_str = substr($gphoto, strpos($gphoto, ",")+1);
$gphotoPath = 'gphotos/'.\Str::random(11) . '.jpg';
$gphoto = base64_decode($base64_str);
\Storage::disk('public')->put($gphotoPath, $gphoto);
$imageArray['gphotos'][] = $gphotoPath;
}
$imageArray['gphotos'] = json_encode($imageArray['gphotos']);
}

I can't trigger a preview when dropping an image into an input field

I have a drop box to upload images to Cloudinary, which works fine, but I can't trigger a preview function when the images are dropped.
This is the HTML code:
<div class="card text-muted">
<label id="upload" for="dropboxInput" class="card-block text-muted">
<input type="file" name="file" data-url="CLOUDINARY_URL"
data-form-data="{CLOUDINARY STUFF}"
data-cloudinary-field="file" class="cloudinary-fileupload"
multiple="multiple" style="display:none" id="dropboxInput">
</label>
</div>
This is the code to trigger the function, which works fine when selecting the images after clicking the drop box:
var dropboxInput = document.getElementById("dropboxInput");
dropboxInput.onchange = function(){readURL(this)};
I have tried this, but it seems that is not getting the dropboxInput.value. The images, once dropped are uploaded:
var dropbox = document.getElementById("dropbox");
dropbox.ondrop = function(){
readURL(dropboxInput);
};
This is the preview function, which handles the preview:
function readURL(input){
if(input.files){
for (i = 0; i < input.files.length; i++) {
var reader = new FileReader();
reader.onload = function(event) {
$($.parseHTML('<img>')).attr('src', event.target.result).appendTo(preview);
}
reader.readAsDataURL(input.files[i]);
}
}
}
In the input field CLOUDINARY STUFF is this:
<input type="file" name="file" data-url="https://api.cloudinary.com/v1_1/MYACCOUNT/auto/upload"
data-form-data="{"eager":"c_limit,h_768,w_1024","signature":"1f5c7426f428ebd02bb45180767fd920716cc59e","api_key":"185516472314647","eager_async":true,"callback":"/cloudinary_cors.html","tags":"Punta de Vistalegre,2","use_filename":true,"timestamp":"1527959323"}"
data-cloudinary-field="file" class="cloudinary-fileupload"
multiple="multiple" style="display:none" id="dropboxInput">
When the images are droped, I don't find the way to pass them to the readURL() function.
Thanks in advance for any help.
When you're doing
dropbox.ondrop = function(){
readURL(dropboxInput.value);
};
Instead of passing dropboxIinput.value try passing only dropboxInput
as you're treating the argument as an input element.
Update 1
I'm not sure I can tell without getting more details. But if by dropping files inside element#dropbox, your input#dropboxInput is getting changed, you could write your function like this:
function readURL(input){
if(input.files.length){
for (i = 0; i < input.files.length; i++) {
image_url = window.URL.createObjectURL(input.files[i]);
$("<img>", {
src: image_url
}).appendTo(preview);
}
}
}
The solution was into the cloudinary-fileupload function, which has the drop option, that handles the dropped files. The input field is generated programmatically with this Java code:
String html = cloudinary.uploader().imageUploadTag("file", options, htmlOptions);
And that's the implementation of the drop option:
$(".cloudinary-fileupload").cloudinary_fileupload({
...
drop: function(e, data){
readURL(data);
},
...

Checking XML Document befor upload to server via php

I am creating a script to upload XML-files to a server, where you enter how often specific tags occur in the file. Now I wanted to automate this, so that i check the XML-file befor uploading, so that the previous manually entered fields are filled automaticly.
I just dont get the point how i can get the content of the file befor actually uploading it - i want to use Javascript for the check and auto-fillment.
The upload is completly realized in php.
Someone have an idea for me?
Edit:
var fileInput;
window.onload = function() {
fileInput = document.getElementById('file_input');
};
function getNodes() {
var anzNodes = fileInput.getElementsByTagName("node").length;
return anzNodes;
}
function getEdges() {
var anzEdges = fileInput.getElementsByTagName("edge").length;
return anzEdges;
}
function fillForm() {
document.getElementById("nodes").value = getNodes();
document.getElementById("edges").value = getEdges();
}
So I have a html-form with the ID file_input. I try to grab that file (which is XML) befor uploading, search it for the amount of "node" and "edge" tags, and autofill these information into the html form. This should happen when the file is chosen. Is this better?
Use the FileReader functionality in JavaScript to grab the document before uploading it. Then you can read the text of the file and pre-process it before sending it to the server.
I found the answer and will provide it for completeness.
My Javascript part:
var openFile = function (event) {
var input = event.target;
var reader = new FileReader();
reader.onload = function () {
var anzEdges = (reader.result.match(/<edge/g) || []).length;
var anzNodes = (reader.result.match(/<node/g) || []).length;
document.getElementById("nodes").value = anzNodes;
document.getElementById("edges").value = anzEdges;
};
reader.readAsText(input.files[0]);
};
and the relevant html-code:
<input type="file" onchange="openFile(event)" name="file_upload" size="60"><br>
<input type="number" id="nodes" name="nodes" min="2" readonly><br>
<input type="number" id="edges" name="edges" min="1" readonly>

Categories