I'm using touchTouch lightbox library in order to preview the images in a lightbox for a post that user wants to send.
This library is used on my back-end and works flawlessly (since the images are in a folder already), but in the front-end there is a form where the user may upload up to 20 images. The images will show their preview after the user has uploaded them (but before they have sent the form) and I want them to be clickable which would open a lightbox library.
The problem is that the library can't find the images location because they are not uploaded just yet.
I thought about using ajax to upload the images into a temporary folder and then remove the folder and its content once the form is submitted. Could this work (without refreshing the page or submitting the form), or are there any better alternative solutions for this?
JavaScript code to upload the files and show their preview + initialize lightbox library upon loading:
function ImgUpload() {
var imgWrap = "";
var imgArray = [];
$('.upload__inputfile').each(function () {
$(this).on('change', function (e) {
imgWrap = $(this).closest('.upload__box').find('.upload__img-wrap');
var maxLength = $(this).attr('data-max_length');
var files = e.target.files;
var filesArr = Array.prototype.slice.call(files);
var iterator = 0;
filesArr.forEach(function (f, index) {
if (!f.type.match('image.*')) {
return;
}
if (imgArray.length > maxLength) {
return false
} else {
var len = 0;
for (var i = 0; i < imgArray.length; i++) {
if (imgArray[i] !== undefined) {
len++;
}
}
if (len > maxLength) {
return false;
} else {
imgArray.push(f);
var reader = new FileReader();
reader.onload = function (e) {
var html = "<div class='upload__img-box'><a href='" + f.name + "' class='light-box' data-group='gallery'><div style='background-image: url(" + e.target.result + ")' data-number='" + $(".upload__img-close").length + "' data-file='" + f.name + "' class='img-bg'><div class='upload__img-close'></div></div></a></div>";
imgWrap.append(html);
iterator++;
touchTouch(document.body.querySelectorAll('.light-box')); // Initialize the lightbox library
// Ajax script here to upload the images to a temporary folder?
}
reader.readAsDataURL(f);
}
}
});
});
});
Basically you could create a data-url from an uploaded image (before uploaded) which will allow you to access it locally and treat it like it was a regular image.
imgInp.onchange = evt => {
const [...files] = imgInp.files
if (files) {
files.forEach(function(file) {
var img = new Image();
img.src = URL.createObjectURL(file)
img.style.width = "200px"
container.appendChild(img)
// do your touchTouch initialization on the new elements
// touchTouch(img);
img.onclick = function() {
alert('lightbox');
}
})
}
}
<form>
<p>Choose Images, then click for lightbox.</p>
<input accept="image/*" type='file' id="imgInp" multiple />
</form>
<div id="container">
</div>
Let's try with given code (not tested):
ImgUpload();
function ImgUpload() {
var imgWrap = "";
var imgArray = [];
$('.upload__inputfile').each(function() {
$(this).on('change', function(e) {
imgWrap = $(this).closest('.upload__box').find('.upload__img-wrap');
var maxLength = $(this).attr('data-max_length');
var files = e.target.files;
var filesArr = Array.prototype.slice.call(files);
var iterator = 0;
filesArr.forEach(function(f, index) {
if (!f.type.match('image.*')) {
return;
}
if (imgArray.length > maxLength) {
return false
} else {
var len = 0;
for (var i = 0; i < imgArray.length; i++) {
if (imgArray[i] !== undefined) {
len++;
}
}
if (len > maxLength) {
return false;
} else {
imgArray.push(f);
var object_url = URL.createObjectURL(f)
var html = "<div class='upload__img-box'><a href='" + f.name + "' class='light-box' data-group='gallery'><div style='background-image: url(" + object_url + ")' data-number='" + $(".upload__img-close").length + "' data-file='" + f.name + "' class='img-bg'><div class='upload__img-close'></div></div></a></div>";
imgWrap.append(html);
iterator++;
setTimeout(function() {
touchTouch(document.body.querySelectorAll('.light-box')); // Initialize the lightbox library
});
// Ajax script here to upload the images to a temporary folder?
}
}
});
});
});
}
<form>
<p>Choose Images, then click for lightbox.</p>
<input accept="image/*" type='file' id="imgInp" class="upload__inputfile" multiple />
</form>
<div id="container">
</div>
Attempt to convert the data obtained from the File interface to Base64 or DataURL.then set img src='data:image/png;base64str'
are there any better alternative solutions for this?
Yes! I would recommend these js libraries
Filepond by PQINA and Dropzone.js
Be advised that they both have image preview but not lightbox feature. But with Dropzone.js, this Stack Overflow thread might help How to add popup image in to dropzone thumbnail?
Related
I have a code which creates a folder in my google drive and allows user to upload multiple files with file upload as a input.
Script.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<script>
(function($){
function floatLabel(inputType){
$(inputType).each(function(){
var $this = $(this);
$this.focus(function(){
$this.next().addClass("active");
});
$this.blur(function(){
if($this.val() === '' || $this.val() === 'blank'){
$this.next().removeClass();
}
});
});
}
floatLabel(".floatLabel");
})(jQuery);
jQuery(".mail-btn").on("click touchstart", function () {
jQuery(this).addClass("fly");
that = this
setTimeout(function() {
jQuery(that).removeClass("fly");
}, 5400)
});
var rootFolderId = 'MYFOLDERID';
var numUploads = {};
numUploads.done = 0;
numUploads.total = 0;
// Upload the files into a folder in drive
// This is set to send them all to one folder (specificed in the .gs file)
function uploadFiles() {
//var allFiles = document.getElementById('myFiles').files;
var allFiles = document.querySelector('input[type=file]').files;
var applicantName = document.getElementById('pname').value;
if (!applicantName) {
window.alert('Missing applicant name!');
}
var applicantEmail = document.getElementById('email').value;
if (!applicantEmail) {
window.alert('Missing applicant email!');
}
var folderName = applicantName + ' ' + applicantEmail;
if (allFiles.length == 0) {
window.alert('No file selected!');
} else {
numUploads.total = allFiles.length;
google.script.run.withSuccessHandler(function(r) {
// send files after the folder is created...
for (var i = 0; i < allFiles.length; i++) {
// Send each file at a time
uploadFile(allFiles[i], r.folderId);
}
}).createFolder(rootFolderId, folderName);
}
}
function uploadFile(file, folderId) {
var reader = new FileReader();
reader.onload = function(e) {
var content = reader.result;
//document.getElementById('files1').innerHTML = 'uploaded '
+ file.name + '...';
//window.alert('uploading ' + file.name + '...');
google.script.run.withSuccessHandler(showSuccess)
.uploadFile(content, file.name, folderId);
}
reader.readAsDataURL(file);
//document.getElementById('Assessment-form').reset();
}
function showSuccess(e) {
$('#forminner').hide();
$('#success').show();
$('#Assessment-form').trigger("reset");
}
</script>
</body>
</html>
form.html
<input type="file" id="myFiles1" class="floatLabel" name="f1" multiple>
<input type="file" id="myFiles2" class="floatLabel" name="f2" multiple>
This allows me to upload multiple files using single file button (only using myFiles1). What I am trying is to upload files in same folder using multiple file buttons(myFiles1 and myFiles2). I tried to change
var allFiles = document.querySelector('input[type=file]').files;
with multiple things such as querySelectorAll but its not working.
I have an aspx page which already has a code for uploading file using "asp:FileUpload" and webform. On a different section of page, I want to add the functionality for a user to upload file. But i do not want to use webforms or "asp:fileupload".
So, I created an HTML file that i inject as an iframe inside a div tag on the aspx.
<div id="iUploadDoc" style="height:50px;">
<iframe name='FileUpload' id='FileUpload' class='iUploadFrame' width='100%' frameborder='0' border='0'src='DocUpload.htm'></iframe></div>
I set the EnablePageMethods to true.
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
And inside the HTML I made a form and with input type file.
<form method="POST" action="DocDetail.aspx.cs" id="docUpload" enctype="multipart/form-data">
<div>
<label for="fileUpload" class="inputLabel">Upload File</label>
<input type="file" id="fileUpload" name="files"/>
</div>
</form>
<div id="progUpload" style="display: none;">
<p class="uploadBox">Uploading...</p>
</div>
<span id= "selectedFile" style="display: none;"></span><span id="fileName" style="display: none;"></span>
Now, I dont know what to put in the "action" param of form.
First on the iframe, below is the script i wrote:
window.onload = load;
function uploadDocFile() {
var prog = document.getElementById("progUpload");
prog.style.cssText = 'display: block;';
var x = document.getElementById("fileUpload");
var txt = "";
var filePath = "";
var fileName = "";
if (x.value == "") {
txt += "Select a file.";
document.getElementById("selectedFile").innerHTML = txt;
} else {
filePath = x.value;
fileName = filePath.replace(/^.*[\\\/]/, '');
txt += "<br>Selected file: ";
document.getElementById("selectedFile").innerHTML = txt;
document.getElementById("fileName").innerHTML = fileName;
}
var formInfo = document.getElementById("docUpload");
document.getElementById("docUpload").style.display = "none";
window.parent.getFormData(formInfo, x ,x.value, fileName);
}
function load() {
var e = document.getElementById("fileUpload");
//var formInfo = document.getElementById("docUpload");
//fakeClick();
e.onchange = function () {
uploadDocFile();
}
}
Then on the parent page, below is the script i wrote.
DocUpload.prototype.uploadFileDoc= function (formInfo, uploadedFile, fileInfo, lastModifiedDate, name, extension, size, type) {
//blacklitsType is an array of types of file (similarly for blacklistExt) that should not be allowed to upload
if (indexOf.call(blackListType, type) < 0 && indexOf.call(blackListExt, extension) < 0) {
var idParams = { OiD: ordId, pID: pId, QID: qId }
var files = uploadedFile.files;
var fileEntries = [];
for (j = 0, len1 = files.length; j < len1; j++) {
file = files[j];
if (file.getAsEntry) {
entry = file.getAsEntry();
} else if (file.webkitGetAsEntry) {
entry = file.webkitGetAsEntry();
}
if (entry) {
isFyl = entry.isFile;
if (!isFyl) {
alert("You can not upload a folder. Uploading files (if present).");
} else {
fileItem = file.getAsFile();
fileEntries.push(fileItem);
}
} else if (!file.type && file.size % 4096 === 0) {
alert("You can not upload a folder. Uploading files (if present).");
} else {
fileEntries.push(file);
}
}
PageMethods.UploadDocument(fileEntries[0], idParams, function (res) {
if (res == true) {
alert("File uploaded successfully.");
} else {
alert("File upload failed.");
}
}, function (err) {
alert("ERROR: " + err._message);
});
} else {
window.alert('You cannot upload incorrect file types.');
}
return;
};
DocUpload.prototype.getFormData = function (formInfo, uploadedFile, fileInfo, nameInfo) {
var currDate, extension, lastModifiedDate, name, nameArr, size, type;
currDate = new Date();
lastModifiedDate = currDate;
type = '';
size = 512;
name = nameInfo;
nameArr = name.split(".");
extension = nameArr[nameArr.length - 1];
DocUpload.prototype.uploadFileDoc(formInfo, uploadedFile, fileInfo, lastModifiedDate, name, extension, size, type);
};
window.getFormData = DocUpload.prototype.getFormData;
The transfer of attributes of form from iframe to parent page work just fine. But how should i post it as file using PageMethod. Below is the page method in my code behind:
[System.Web.Services.WebMethod()]
[System.Web.Script.Services.ScriptMethod(UseHttpGet = false)]
public static bool UploadDocument(HttpPostedFileBase uploadedFile,IdParams idParams) {
bool err = false;
try{
//code
err= true;}
catch(Exception ex){err = false;}
return err;
}
No matter how much tried, either I keep getting error regarding HTTPPostedFileBase or Serialization of child not allowed.Below are only some of the errors i keep getting (not at the same time):
No parameterless constructor System.Web.HttpPostedFileBase aspx, OR
The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter
What should i do?
I am working with javascript. I have append html like
<input type="file" class="browse-02 validate[required]" onchange="showMyImage1(this,'thumbnil4')" name="data[question1][question_image4]" id="" />
<img id="thumbnil4" src="images/no_status_image_sp.jpg" class="uploadimg" alt="">
This html comes dynamically when click on add more button.
I have written js as shown in below :
function showMyImage1(fileInput,id) {
//console.log(fileInput);
var files = fileInput.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imageType = /image.*/;
if (!file.type.match(imageType)) {
continue;
}
//alert(id);
var img = document.getElementById(id);//$("#"+id)[0];
//alert(img);
console.log(img);
img.file = file;
var reader = new FileReader();
reader.onload = (function(aImg) {
//alert(aImg);
return function(e) {
aImg.src = e.target.result;
};
})(img);
reader.readAsDataURL(file);
}
}
Here, this js does not work for appended data. It gives an error at img.file = file; line as img is null.
So what javascript should I have to write?
UPDATE
I modified your code a little bit, the problem why it is not working because the elements are created dynamically in DOM. So normal event listener will not trigger.
To listen events from dynamically created elements, use Jquery on API.
$(document).on('change','.browse-btn', function(){
var thumbnailId = $(this).prev().attr('id');
showMyImage1(this, thumbnailId)
});
$(document).ready(function() {
var n = 5;
var i = 2;
var j = 10;
var k = 2;
$('.addmore').click(function(e){
e.preventDefault();
var div = $('#appendData');
var html = '';
html += '';
html += '<div class="formPartd">';
html += '<div class="formPartd_left">';
html += '<div class="upload-img">';
html += '<label>Upload Image'+n+'</label>';
html += '<span>';
html += '<img id="thumbnil'+j+'" src="" class="uploadimg" alt="">';
html += '<input type="file" class="browse-btn" name="data[question1][question_image'+n+']" />';
html += '</span>';
html += '<div class="clear10"></div>';
html += '</div>';
html += '<div class="fildtpart3 pos">';
html += '<label>Answer'+n+'</label>';
html += '<span><input type="text" class="validate[required]" id="" value=""></span>';
html += '<div class="clear10"></div>';
html += '</div>';
html += '</div>';
div.append(html);
i++;j++;n++;
// $(document).trigger('updated');
});
$(document).on('change','.browse-btn', function(){
var thumbnailId = $(this).prev().attr('id');
showMyImage1(this, thumbnailId)
})
});
function showMyImage1(fileInput,id) {
//console.log(fileInput);
var files = fileInput.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imageType = /image.*/;
if (!file.type.match(imageType)) {
continue;
}
//alert(id);
var img = document.getElementById(id);//$("#"+id)[0];
//alert(img);
//console.log(img);
img.file = file;
var reader = new FileReader();
reader.onload = (function(aImg) {
//alert(aImg);
return function(e) {
aImg.src = e.target.result;
};
})(img);
reader.readAsDataURL(file);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="appendData"></div>
<div class="fildtpart2" style="float:right">
Add More
</div>
The onchange event is called when user select a file, but at this time the file itself won't be accessible. To show it, you need the user to post it, usually via a <form>, then saving it. From there, you will be able to show it.
On many modern site, this process looks like being transparent, due to the use of AJAX call :
The onchange event call a jQuery function, that actually send the form (and the image) to a php file. This file save the image, then return the corresponding <img> tag, filled with the correct attribute src, that will be append on the page via the Ajax callback.
So I'm doing a simple multiple image upload script using javascript, but socket.io has to be used in order to get the image into the database. In order to run previews I have been taking event.target.result and putting it as the image src on a div. Is there any way I can store the this in an array for each image so that I can transfer it over the socket, and have it load on the other side? When I try to load it into an array, it's always undefined.
for (var i = 0; file = files[i]; i++) {
name[i] = files[i].name;
// if the file is not an image, continue
if (!file.type.match('image.*')) {
continue;
}
reader = new FileReader();
reader.onload = (function (tFile) {
return function (evt) {
var div = document.createElement('div');
var miniDiv = document.createElement('div');
div.id = "photoDiv";
div.innerHTML = '<img style="width: 120px; height: auto;" src="' + evt.target.result + '" />';
div.className = "photos";
var data = evt.target.result;
picture[i] = data;
document.getElementById('filesInfo').appendChild(div);
document.getElementById('previewDiv').appendChild(document.getElementById('filesInfo'));
};
}(file));
reader.readAsDataURL(file);
}
uploadFiles();
}
Don't make functions within a loop like that, it can lead to unexpected things.
I would suggest using JSHint, it's very helpful.
You made two mistakes:
1) You should pass i variable to your closure together with file.
2) The most important: reader.onload is a function that will be called not immediately, but in some delay, and as a result it will be called after uploadFiles() call. That's why you get an empty picture.
Try to rewrite your code as follows:
var done = 0;
var picture = [];
for (var i = 0; file = files[i]; i++) {
name[i] = files[i].name;
// if the file is not an image, continue
if (!file.type.match('image.*')) {
if (++done === files.length) {
uploadFiles();
}
continue;
}
reader = new FileReader();
reader.onload = (function (tFile, index) {
return function (evt) {
//[...]
picture[index] = data;
//[...]
if (++done === files.length) {
//the last image has been loaded
uploadFiles();
}
};
}(file, i));
reader.readAsDataURL(file);
}
HTML Code
<input type="file" accept="image/*" multiple webkitdirectory mozdirectory msdirectory odirectory directory id="fileURL"/>
Javascript Code:
var files,
file,
extension,
sum,
input = document.getElementById("fileURL"),
output = document.getElementById("fileOutput"),
holder = document.getElementById("fileHolder")
sizeShow = document.getElementById("filesSize");
input.addEventListener("change", function (e) {
files = e.target.files;
output.innerHTML = "";
sum = 0;
for (var i = 0, len = files.length; i < len; i++) {
file = files[i];
extension = file.name.split(".").pop();
if(extension=="jpg"||extension=="png"){
var size = Math.floor(file.size/1024 * 100)/100;
sum = size+sum;
output.innerHTML += "<li class='type-" + extension + "'>"+file.webkitRelativePath + file.name + " (" + size + "KB)</li>";
}else{
file.remove();
}
}
if(sum<1024*1024){
sizeShow.innerHTML = Math.floor(sum/1024*100)/100 + " MB";
}else if(sum>1024*1024){
sizeShow.innerHTML = Math.floor(sum/1024*1024*100)/100 + " GB";
}
}, false);
How do i get just the image in the file upload? accept="image/*" doesn't work for directory.
This does work but the statement file.remove() doesn't work at all.
I guess the input:file is read-only.
How do i solve this?
You can set input.files to a FileList (obtained from e.g. drag and drop), but you cannot create/modify a FileList. So you cannot modify the files of an input to e.g. only contain images.
What you can do, though, is uploading manually (through ajax), and only send files that have a type starting with "image/". See http://jsfiddle.net/WM6Sh/1/.
$("form").on("submit", function(e) {
e.preventDefault();
var files = $(this).find("input").prop("files");
var images = $.grep(files, function(file) {
return file.type.indexOf("image/") === 0; // filter out images
});
var xhr = new XMLHttpRequest();
xhr.open("POST", "/", true);
$(xhr).on("readystatechange", function(e) {
if(xhr.readyState === 4) {
console.log("Done");
}
});
var data = new FormData();
$.each(images, function(i) {
data.append(i, this); // append each image file to the data to be sent
});
console.log(
"Sending %d images instead of all %d files...",
images.length,
files.length
);
xhr.send(data);
});