Fill the file details when dropped into input file - javascript

I have made a drag and drop file upload. Now when a file is dropped in the drop zone it should fill the data of the dropped file into <input type="file">. How to do it ?
HTML
:
<div id='slct'>
Select file <input type="file" id="filename"
onchange="readURL(this);" /> <input type="button" value="Upload"
onclick="sendFile()" /><br> Or
</div>
<button id='uploadDrp'>Upload</button>
<div class="drop">
<div id="filedrag">drop file here</div>
<img src="#" id="prof">
</div>
JavaScript :
obj.on("dragover", function(e) {
e.stopPropagation();
e.preventDefault();
$(this).css("border", "1px solid #00aaff");
});
obj.on("drop", function(e) {
e.stopPropagation();
e.preventDefault();
$(this).css("border", "1px solid #00aaff");
$("#slct").hide();
$("#uploadDrp").fadeIn(500);
var dt = e.originalEvent.dataTransfer;
file01 = dt;
var files = dt.files;
if (dt.files.length > 0) {
if (dt.files && dt.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$('#prof').attr('src', e.target.result).width(200)
.height(200);
$('#prof').fadeIn();
};
reader.readAsDataURL(dt.files[0]);
}
}
});

History lession
It s like #adeneo said,
You can't set the value of a file input, due to security restrictions
But in some browser you can set the value to a empty string to clear the field.
In older (IE) browser you need to replace the DOM-node completely
However there is one way to change the input-file-value and that is with another instance of FileList
but you do not set the value, instead you set the input.files property: see jsfiddle demo here
I filled a bug report long ago to ask if we could get access to construct a new FileList
We had it globaly in window but there is no way we can use it...
var file1 = new File(['foo'], 'foo.txt')
var file2 = new File(['bar'], 'bar.txt')
new window.FileList([file1, file2]) // throws Illegal constructor
There response was that they are moving towards a normal array instead.
It s being descussed here to change it. But there is little to no progress in that field...
Solution
Unfortenly you can not change the value and sumbit the form, Instead you need to create a own FormData and post it using ajax
Then you can redirect to the target url...
// Make your own FormData
var fd = new FormData
// Add all drang'n'drop files
for (let file of dt.files)
fd.append('files', file)
// Add all input files
for (let file of input.files)
fd.append('files', file)
// upload it using xhr or the new fetch api
fetch(targetUrl, {method: 'post', body: fd}).then(redirect)
Code review
if (dt.files.length > 0) {
// it's unnecessary to have two if statement that checks the same thing
if (dt.files && dt.files[0]) {
// Using FileReader is unnecessary and also worse
// see this performance test: https://jsfiddle.net/fdzmd4mg/1
var reader = new FileReader();
reader.onload = function(e) {
$('#prof').attr('src', e.target.result).width(200).height(200);
$('#prof').fadeIn();
};
reader.readAsDataURL(dt.files[0]);
}
}
What i think you should do instead is something like this
if (dt.files && dt.files[0]) {
// store the file list somehere to upload them later
// window.files = Array.from(dt.files)
// window.files = [dt.files[0]]
let url = URL.createObjectURL(dt.files[0])
$('#prof').attr('src', url).width(200).height(200);
$('#prof').fadeIn();
}

Related

Displaying picture before submitting the form JavaScript [duplicate]

This question already has answers here:
Preview an image before it is uploaded
(29 answers)
Closed 2 years ago.
I'd like to prepare a profile picture upload form. What's necessary here? Ability to see how is the image going to fit, before submitting it which is the moment I'm stuck at unfortunately. I attached here the form element from my JSP and JS file supposed to handle retrieving the form file via formData.
For some reason tho JS isn't willing to do anything with the backgroundImage property not throwing any kind of error either.
I'd really love some help with this task.
Using plain languages only, as I'm an extremist ;).
HTML:
<div class="profilePicture">
<form class="choosePhotoLabel" enctype="multipart/form-data" action="savePictureServlet" method="post">
<input class="choosePhoto" type="file" name="picture">
<input class="photoOk" type="submit" value="this is the picture!">
</form>
</div>
JavaScript:
document.addEventListener("DOMContentLoaded", function () {
let formInHTML = document.getElementsByClassName("choosePhotoLabel").item(0);
let formInputInHTML = document.getElementsByClassName("choosePhoto").item(0);
let imagePlace = document.getElementsByClassName("profilePicture").item(0);
let pictureReader = new FileReader();
function mlemToFile(mlem) {
mlem.lastModifiedDate = new Date();
mlem.name = "nonMlemPicture";
return mlem;
}
formInputInHTML.addEventListener("change", function () {
let formData = new FormData(formInHTML);
let formPicture = formData.get("picture");
if (formPicture != null) {
let nonMlemPicture = mlemToFile(formPicture);
pictureReader.readAsDataURL(nonMlemPicture);
if (pictureReader.result != null) {
let picture64 = pictureReader.result.replace(/^data:.+;base64,/, '');
imagePlace.style.backgroundImage = picture64;
}
}
});
});
The FileReader is async, which means you have to wait for the result to have been read.
you are missing:
pictureReader.onload = function () {
// code to set the image source
}
also you shouldn't remove the prefix (data:.+;base64) in the beginning - that part is important
But i don't think you should be using the filereader to display base64 images. It's better & faster to use object url referring to the blob/file in it's binary form.
also, instead of using formdata, use formInputInHTML.files[0]
formInputInHTML.addEventListener("change", function () {
const formPicture = formInputInHTML.files[0];
const url = URL.createObjectURL(formPicture);
imagePlace.style.backgroundImage = `url(${url})`;
});
short solution
let uploadInput = document.getElementById("uploadInput");
uploadInput.onchange = function () {
let image = new FileReader();
image.onload = function (e) {
document.getElementById("imagePreview").src = e.target.result;
};
image.readAsDataURL(this.files[0]);
};
<input type="file" id="uploadInput" />
<img id="imagePreview" style="width:300px;" class="normal" />

How to delete the specific value of an “input type=file” multiple

I am using input type='file' with multiple file and one with single file. like,
//single image
//IMAGE_TYPES is constant and defined with:define('IMAGE_TYPES',array('main','floor','bedroom1','bedroom2','bedroom3','kitchen','reception','garages','epc','other'));
#foreach(IMAGE_TYPES as $images)
#if($images!='other')
<div class="col-sm-10">
<input type="file" class="form-control" id="{{$images}}_image" name="{{$images}}_image" accept="image/*" placeholder="<span> <i class='fa fa-plus-circle'></i>Click here or drop files to upload</span>"/>
</div>
#else
//multiple
<div class="col-sm-10">
<input type="file" class="form-control" id="other_images" name="other_images[]" accept="image/*" placeholder="<span> <i class='fa fa-plus-circle'></i>Click here or drop files to upload</span>" multiple />
</div>
#endif
#endforeach
Now, I validating it with jquery like,
var image_type ='<?=json_encode(IMAGE_TYPES);?>';
image_type = JSON.parse(image_type);
var max_image_size = 2;
$.each(image_type, function( index, value ) {
if (value!='other') {
$('#'+value+'_image').bind('change', function() {
var a=(this.files[0].size);
var ValidImageTypes = ["image/jpeg", "image/png"];
if ($.inArray(this.files[0].type, ValidImageTypes) < 0) {
show_notification('error','Only .jpg/.jpeg and .png file allowed. Please select other image.');
var file = document.getElementById(value+'_image');
file.value = file.defaultValue;
return false;
}
else{
if (Math.round(a / (1024 * 1024)) > max_image_size) {
show_notification('error','Image is Greater than '+max_image_size+'MB. Please select smaller image.');
var file = document.getElementById(value+'_image');
file.value = file.defaultValue;
return false;
}
else
{
preview_main_image(value);//won't matter
}
}
});
}
else{
$('#other_images').bind('change', function() {
$('div.add_preview').remove();//won't matter
for (var i = 0; i < $("#other_images").get(0).files.length; i++) {
var a=(this.files[i].size);
var name = this.files[i].name;
var ValidImageTypes = ["image/jpeg", "image/png"];
if ($.inArray(this.files[i].type, ValidImageTypes) < 0) {
show_notification('error','Image '+name+' is Not allowed. Only .jpg/.jpeg and .png file allowed. Please select other image.');
}
else{
if (Math.round(a / (1024 * 1024)) > max_image_size) {
show_notification('error','Image '+name+' is Greater than '+max_image_size+'MB. Please select smaller image.');
}
else
{
$('#other_image_preview').append("<div class='col-md-2 p_3 add_preview'><img class='img-responsive' src='"+URL.createObjectURL(event.target.files[i])+"'></div>");//won't matter
//preview_detail_images(value);
}
}
}
});
}
});
Now, my question is when i am using single image if image is not fitting in validation then i delete it's value from input type='file' using, this code
var file = document.getElementById(value+'_image');
file.value = file.defaultValue;
return false;
But when i select multiple image and if any image is not fitting in validation then how can i remove that particular image from input type='file'.
Please help me
The file will have to come in input element for the input change handler to work. You can validate there and show only valid files in preview, ignoring the invalid ones.
You can check jQuery file uploader: https://blueimp.github.io/jQuery-File-Upload/
You can keep your input invisible over another div which is your preview and show the uploaded files in the div to give the illusion to the user that you are discarding invalid files.
The answer is simple: You can't. Value of files property of an <input type="file"> is a FileList. This one is immutable for security reasons. Also the files property is readonly and you can't construct a FileList.
The best you could do is to a) show a validation error to user and ask him to remove the file; b) ignore the file on processing (e.g. preview, uploading to server).
As #mixable already pointed out in his answer, validation should also be done on backend.
You can just ignore this file type on the server when processing the uploaded files. This is the better solution, because it is more secure. When you rely on JavaScript, it is very easy to send manipulated data to your server and upload filetypes of other images (or even scripts like js, php, ...).
Hi please check out my fiddle. I created a form which can be automatically submitted with valid files.
https://jsfiddle.net/2ah5r0bj/135/
What I did is basically:
var form = document.getElementById("myAwesomeForm");
var formDataToUpload = new FormData(form);
...
for (var i = 0; i < validFiles.length; i++) {
formDataToUpload.append("other_images[]", validFiles[i], validFiles[i].name);
}
var xhr = createCORSRequest("POST", "https://httpbin.org/post");
xhr.send(formDataToUpload);

How to make sure File is read completely by FileReader using Javascript

I have various HTML elements defined in an XML file.
I cant be able to display my XML element as whole but it has multiple rows and each row consists of checkboxes, File upload option, etc.
I am using Javascript to get these elements and then using XMLHTTPRequest, sending these requests to the controller to process.
Imagine HTML elements be like below:
Row1 ---- Checkbox1_Row1 TextDescription_Row1 FileUpload_Row1
Row2 ---- Checkbox1_Row2 TextDescription_Row2 FileUpload_Row2
I can have how many ever rows as possible.
Using Javascript, I am getting all these form elements and these elements are differentiated by Row number (Row1, Row2).
I am looping through each form elements and then
for(var j=0; j< formelements.length; j+++)
{
if (formElements[j].type == "textbox")
{
Do something
}
elseif (formElements[j].type == "file")
{
var Base64String;
var ready = false;
var fileName = formElements[j].files[0].name;
var check = function () {
if (ready === true) {
array.push(Base64String);
return;
}
setTimeout(check, 1000);
}
check();
var reader = new FileReader();
reader.onloadend = function (evt) {
Base64String = evt.target.result;
ready = true;
};
reader.readAsDataURL(file);
}
}
I am using an array to push all the values corresponding to each row and the array with final value will be sent to the controller after some alterations. Here for file upload option, I am reading the file from each row and converting them into binary format and sending to the controller. This approach works fine, if there is only one row. What happens with this approach when there are multiple rows is, while looping through the form element, it check everything for the first row (say textbox) and puts into the array but when it is file type, it goes to the loop and reads the file. Reading the file takes sometime here and by the time loop goes to the next form element (which is nothing but Row2). Now Row2 form element comes into picture and say, we do not upload any file, it will be null. Now check() function gets completed and file from row1 is read completely. Since the loop is already in for Row 2 form element, this file value is getting assigned to Row2 apart from null values. So Row2 will have both null value and file value when it comes to file type but there is no value for Row1. Similarly if I have many files in multiple rows, the file value gets assigned to which ever row form element that is there in current loop based on the time read by FileReader.
I need to make sure that file value is read completely before moving on to the next form element. How to achieve this?
************************Updates**********************
The question which was referred here marking mine as duplicate has only file type coming in and hence, they can loop through the file type. For me, form elements consists of Checkbox1_Row1, TextDescription_Row1, FileUpload_Row1, Checkbox1_Row2 , TextDescription_Row2, FileUpload_Row2.
I have to make sure that FileUpload_Row1 has right value read from the file before moving on to to next form element, here Checkbox1_Row2.
evt should be event at evt.target.result. .push() event.target.result to fileList array, do stuff when fileList .length is equal to count
<!DOCTYPE html>
<html>
<head>
<script>
function myFunction() {
var files = Array.prototype.map.call(
document.querySelectorAll("[id^=myFile]")
, function(input) {
return {id:input.dataset.id, file: input.files[0]};
});
var count = files.length; // total number of files
var fileList = []; // accepted files
for (var i = 0; i < count; i++) {
var file = files[i].file;
var id = files[i].id;
var filename = files[i].file.name;
if (i >= count) {
break;
}
var reader = new FileReader();
reader.onload = (function(id, filename) {
return function(event) {
fileList.push({id, filename, file:event.target.result}); {
if (fileList.length === count) {
// do stuff with `fileList`
console.log(fileList);
}
}
}
})(id, filename);
reader.readAsDataURL(file);
}
}
</script>
</head>
<body>
<h1>Hello Plunker!</h1>
<input type="file" id="myFile_row1" data-id="A">
<input type="file" id="myFile_row2" data-id="B">
<input type="file" id="myFile_row3" data-id="C">
<button onclick="myFunction()">Try it</button>
</body>
</html>
plnkr http://plnkr.co/edit/VCGPPbWcock0PgC9wMWi?p=preview

multiple file input html not working

I have the following code for multiple file input
<form action="" enctype = "multipart/form-data" method="post" name="login">
<input type = "file" name = "photo[]" id = "files" multiple onchange = "handleFileSelect(this.files)"/><br/>
<div id="selectedFiles"></div>
<input type="submit" value="Sign In">
</form>
The javascript equivalent function is.
selDiv = document.querySelector("#selectedFiles");
function handleFileSelect(e) {
if(!this.files) return;
selDiv.innerHTML = "";
var files = e;
for(var i=0; i<files.length; i++) {
var f = files[i];
selDiv.innerHTML += f.name + "<br/>";
}
}
What I am getting is upon uploading the second file. The FileList gets overwritten and instead of having 2 files, second file is present in the FileList. Here FileList is passed by this.files.
Also upon passing to the server only second image is passed. I have googled throughly but could not find answer. I would appreciate if anyone could help.
...multiple file input ... The FileList gets overwritten...
Actually that's how the HTML file input with the multiple attribute works—the user must select all the files they want to upload at once, using shift or control click. If the user operates the same file input upload process a second time anything selected prior is discarded and only the most recent selections remain in the FileList.
But isn't there any way for the user upload file multiple times.
To let your site users use an HTML file input element multiple times and keep all the previous selections, you'll need to write to hidden form elements the file (base64 data) received each time the file element is used.
For example:
<form action="process.php" method="post" name="uploadform" enctype="multipart/form-data">
// other form elements if needed
<input type="submit">
</form>
<!-- outside the form, you don't want to upload this one -->
<input type="file" id="upfiles" name="upfiles">
<script>
document.getElementById('upfiles').addEventListener('change', handle_files, false);
function handle_files(evt) {
var ff = document.forms['uploadform'];
var files = evt.target.files;
for ( var i = 0, file; file = files[i]; i++ ) {
var reader = new FileReader();
reader.onload = (function(file) {
return function (ufile) {
var upp = document.createElement('input');
upp['type'] = 'hidden';
upp['name'] = +new Date + '_upfile_' + file.name.replace(/(\[|\]|&|~|!|\(|\)|#|\|\/)/ig, '');
upp.value = ufile.target.result;
ff.appendChild(upp);
}
}(file));
reader.readAsDataURL(file);
}
}
</script>
Next, you need to write a script to run on the server to process the hidden base64 fields. If using PHP you can:
<?php
$path = 'path/to/file/directory/';
// this is either:
// - the absolute path, which is from server root
// to the files directory, or
// - the relative path, which is from the directory
// the PHP script is in to the files directory
foreach ( $_POST as $key => $value ) { // loop over posted form vars
if ( strpos($key, '_upfile_') ) { // find the file upload vars
$value = str_replace(' ', '+', $value); // url encode
file_put_contents($path.$key, base64_decode($value));
// convert data to file in files directory with upload name ($key)
}
}
?>
I ran into the same problem. Thanks for the question and answer. I managed to add several files by adding to the DOM input type file and delegating the click to the detached element :
<form method="POST" enctype="multipart/form-data" action="/echo/html">
<button class="add">
Add File
</button>
<ul class="list">
</ul>
<button>
Send Form
</button>
</form>
With the javascript :
$('form button.add').click(function(e) {
e.preventDefault();
var nb_attachments = $('form input').length;
var $input = $('<input type="file" name=attachment-' + nb_attachments + '>');
$input.on('change', function(evt) {
var f = evt.target.files[0];
$('form').append($(this));
$('ul.list').append('<li class="item">'+f.name+'('+f.size+')</li>');
});
$input.hide();
$input.trigger('click');
});
It is working with Edge, Chrome 50 and firefox 45, but I don't know the compatibility with older versions or other browsers.
See the this fiddle.

How to reset <input type='file' /> in ASP.NET MVC

I just checked all options to reset/clean <input type='file' /> based on
Clearing <input type='file' /> using jQuery
And all of them are not working.
What I do in my page is submit new file and update page in a normal way.
HTML
<form>
<input type="file" name="file" id="fileUploader" class="btn btn-sm btn-default" />
</form>
And then I have AJAX link to delete uploaded file. So I delete it in Session and in AJAX callback do like
//Reset
var uploader = $("#fileUploader");
window.reset = function (uploader) {
e.wrap('<form>').closest('form').get(0).reset();
e.unwrap();
}
uploader.reset();
// Clear all file fields:
// $("input:file").clearInputs();
// Clear also hidden fields:
//$("input:file").clearInputs(true);
//$row.replaceWith($row = $row.val('').clone(true));
But after this when I do F5 / Update HttpPost happens and it has posted file.
How to fix it?
P.S.
And even if I do the following no joy...
$('#inputFileForm').html("");
$('#inputFileForm').append(
// <input type="file" name="file" id="fileUploader" class="btn btn-sm btn-default" />
$('<input/>').attr('type', 'file').attr('name', 'file').class("btn btn-sm btn-default")
);
The only sure way to clear a file input is to replace the element, as some browsers (mostly IE) will not allow you to reset the value of a file input
var input = $("#fileUploader");
input.replaceWith(input.clone(true));
This keeps the events but clears the input value (values are not cloned)
I'm going to step outside of the box for a moment and offer a solution to your problem that uses the FormData() object. It will store multiple files as a key/value. You'll want to make sure your target audience can support it (check Can I Use for reference). Per MDN,
XMLHttpRequest Level 2 adds support for the new FormData interface.
FormData objects provide a way to easily construct a set of key/value
pairs representing form fields and their values, which can then be
easily sent using the XMLHttpRequest send() method. [Source: MDN]
First, you'll want to instantiate the object to store your files. We'll create a new FormData() object:
var formData = new FormData();
Next, you'll want to iterate through each file in your file input box:
// your input
var myInput = document.getElementById("fileUploader");
for (i = 0; i < myInput.files.length; i++) {
// iterate through each file in the input
// and append it to the new FormData() object
formData.append(myInput.files[i].name, myInput.files[i]);
// files[i].name will assign the key of "myfile.png"
// files[i] will assign the value of your your file
}
Now you have all of your files in one object that can be uploaded directly via XMLHttpRequest or $.ajax. For example:
var xhr = new XMLHttpRequest();
xhr.open('POST', ajaxPostPath);
xhr.send(formData);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
// hooray! it worked!
}
}
Now, all you need to do is instantiate a new FormData() object each time you need to submit.
So, if you have a submit button...
$(document).ready(function() {
$("#mySubmitBtn").on("click", function() {
var myInput = document.getElementById("fileUploader");
for (i = 0; i < myInput.files.length; i++) {
formData.append(myInput.files[i].name, myInput.files[i]);
}
var xhr = new XMLHttpRequest();
xhr.open('POST', ajaxPostPath);
xhr.send(formData);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
// hooray! it worked!
}
}
});
});
Again, this is entirely another route to take with this but it'll allow you to completely have control over what files are being sent to the server without having to worry about the inconsistencies of IE/Chrome/FF and resetting of the File input type. If you'd like to see an example, I have a semi readable script you can look at here.

Categories

Resources