socket.io image upload via javascript - javascript

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);
}

Related

Why is my function only applying SRC property only to the first uploaded image?

The function successfully creates N image elements with a class of new-avatar-picture, however, it only adds SRC property to the first image. I'm not getting any errors in the console either.
function displayInputImage(input) {
var files = input.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var reader = new FileReader();
var x = document.createElement("img");
reader.onload = function(e) {
x.setAttribute("src", e.target.result);
}
reader.readAsDataURL(file);
x.className = "new-avatar-picture";
$('.upload-btn-wrapper').append(x);
}
}
The issue with your logic is due to the fact that onload() of the reader fires after the loop completes, so x will refer to the last element in the set. Hence that single element gets its src set N times.
To fix this you could use a closure:
function displayInputImage(input) {
for (var i = 0; i < input.files.length; i++) {
var $img = $("<img />");
(function($imgElement) {
var reader = new FileReader();
reader.onload = function(e) {
$imgElement.prop("src", e.target.result);
}
reader.readAsDataURL(input.files[i]);
$imgElement.addClass("new-avatar-picture");
$('.upload-btn-wrapper').append($imgElement);
}($img));
}
}
Alternatively you could create the new img elements only after the content of the file is read:
function displayInputImage(input) {
for (var i = 0; i < input.files.length; i++) {
var reader = new FileReader();
reader.onload = function(e) {
$('<img />').addClass('new-avatar-picture').prop('src', e.target.result).appendTo('.upload-btn-wrapper');
}
reader.readAsDataURL(input.files[i]);
}
}
One way to do that is to give each image a new property, I call it temp_src so that the browser will not try to load the images right away.
Then in the .onload event, loop through all images that you have created and give each of them the proper src value, by copying it from its temp_src property.
Something like:
var reader = new FileReader();
function displayInputImage(input) {
var files = input.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var x = document.createElement("img");
x.setAttribute("class", "temp_img");
x.setAttribute("temp_src", file);
reader.readAsDataURL(file);
x.className = "new-avatar-picture";
$('.upload-btn-wrapper').append(x);
}
}
reader.onload = function(e) {
var images = document.getElementsByClassName("tmp_img");
images.forEach(function(img) {
img.setAttribute("src", img.temp_src);
});
}

Javascript file reader does not get stored in array

I am trying to upload multiple images. So I read that I can generate a temporary url and send them with ajax.
The idea is push the url created with filereader into an array and the send with ajax but the url's are not pushed properly. When I see the result I got like an empty array:
But if I click the arrow I can see the url's inside
But them seems Inaccessible.
This is my code:
$('form').on('submit',function(e) {
e.preventDefault();
var filesToUpload = document.getElementById("myFile");
var files = filesToUpload.files;
var fd = new FormData();
var arr = [];
if (FileReader && files && files.length) {
for (i=0; i< files.length; i++){
(function(file) {
var name = file.name;
var fr = new FileReader();
fr.onload = function () {
arr.push(fr.result);
}
fr.readAsDataURL(file);
})(files[i]);
}
console.log(arr);
}
});
The final idea is convert to string JSON.stringify(arr) and then parse in php json_decode($_POST['arr']).
Of course this is not working because JSON.stringify(arr) gets empty.
Maybe the following simple solution works for you? I placed your console.log() and your ajax call into the fr.onload() method but fire it only, after your results array has been filled up with all values:
$('form').on('submit',function(e) {
e.preventDefault();
var filesToUpload = document.getElementById("myFile");
var files = filesToUpload.files;
var fd = new FormData();
var arr = [];
if (FileReader && files && files.length) {
for (var i=0; i< files.length; i++){
(function(file) {
var name = file.name;
var fr = new FileReader();
fr.onload = function () {
arr.push(fr.result);
if(arr.length==files.length) {
console.log(arr);
// place your ajax call here!
}
}
fr.readAsDataURL(file);
})(files[i]);
}
}
});

Uncaught DOMException: Failed to execute 'readAsDataURL' on 'FileReader': The object is already busy reading Blobs.(…)

I am working on this for the past 6 hours, and I am unable to figure this out.
I am trying to create a website, in which I choose a folder of images and I show them in my document. I get the first image to show, and then I get the following error in my console.
Uncaught DOMException: Failed to execute 'readAsDataURL' on 'FileReader': The object is already busy reading Blobs.(…)
I believe the issue is caused due to my for loop because the filereader is asynchronous. But I need to loop through the whole array for this, so what am I doing wrong?
I load the files ("will check to make sure I get only images later"), into an array, and then I read each file one at a time.
Before breaking down my code to functions I did everything in a single function and it works! I included the HTML + the original and current JS code. Thank you for taking the time to see this.
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tiled Image Viewer</title>
<script src="js/tiv.js"></script>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="main-wrap">
<form name="uploadForm">
<input id="images" type="file" webkitdirectory mozdirectory directory name="myFiles"
onchange="readAndShowFiles();" multiple/>
<span id="list"></span>
</form>
</div>
</body>
</html>
Javascript Original:
function readAndShowFiles() {
var files = document.getElementById("images").files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
// Have to check that this is an image though
// using the file.name TODO
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(file) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(file);
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
}
Javascript Current:
function readAndShowFiles() {
console.log("Checkpoint2"); //test
var tiv = new tivAPI();
var array = tiv.getLoadedImages();
tiv.showLoadedImages(array);
}
function tivAPI(){
var imagesarray = new Array();
return{
loadImages: function(){
console.log("Loading Files"); //test
var files = document.getElementById("images").files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
// Have to check that this is an image though
// using the file.name TODO
}
console.log(files.length); //test
return files;
},
getLoadedImages: function(){
imagesarray = this.loadImages();
console.log("Returning Files"); //test
console.log(imagesarray.length);
return imagesarray;
},
showLoadedImages: function(elem){
console.log("Showing Files"); //test
var files = elem;
var reader = new FileReader();
// Closure to capture the file information.
for (var i = 0; i < files.length; i++) {
var file = files[i];
reader.onload = (function(file) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(file);
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
}
};
}
The reason why your code fails is that you are using the same reader variable on each subsequent loop iteration.
When Javascript parses your code, what happens is that all variables get moved to the top of the function, so your parsed code looks something like this:
function readAndShowFiles() {
var files = document.getElementById("images").files;
var reader;
var file;
var i;
for (i = 0; i < files.length; i++) {
file = files[i];
reader = new FileReader();
reader.onload = (function(file) {
return function(e) {
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'
].join('');
document.getElementById('list').insertBefore(span, null);
};
})(file);
reader.readAsDataURL(file);
}
}
You could avoid this error quite simply by just moving all the logic inside the anonymous function like so:
function readAndShowFiles() {
var files = document.getElementById("images").files;
for (var i = 0; i < files.length; i++) {
// Closure to capture the file information.
(function(file) {
var reader = new FileReader();
reader.onload = function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'
].join('');
document.getElementById('list').insertBefore(span, null);
};
// Read in the image file as a data URL.
reader.readAsDataURL(file);
})(files[i]);
}
}
Now you are using a unique reader variable for each file iteration. Note also that this could be simpler if you just did:
array.from(files).forEach(function(file) {
// code
})
Or just used the let variable (that way you will use a different FileReader every time):
function readAndShowFiles() {
var files = document.getElementById("images").files;
for (let i = 0; i < files.length; i++) {
let file = files[i];
let reader = new FileReader();
reader.onload = function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'
].join('');
document.getElementById('list').insertBefore(span, null);
};
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
}
The for loop could be written easier with es6 leaving you with 2 less variables
function readAndShowFiles() {
var files = document.getElementById("images").files;
for (let file of files) {
let reader = new FileReader();
reader.onload = function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'
].join('');
document.getElementById('list').insertBefore(span, null);
};
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
}
But if you want it super easy, why not just skip the FileReader altogether and avoid all functions & callbacks with URL.createObjectURL? Using createObjectURL you save CPU de/compiling the file to/from base64:
function showFiles() {
var files = document.getElementById("images").files;
for (let file of files) {
let img = new Image;
img.src = URL.createObjectURL(file);
img.title = file.name;
document.getElementById('list').appendChild(img);
}
}
the Problem occured because I was trying to use the same reader for every image.
Moving var reader = new FileReader();into the loop (ShowLoadedImages function), solved the issue and showed all the images like it was supposed to.

How to read files from folder

Found this article which showing how to distinguish file upload from directory How to handle dropped folders but they not explain how I can handle the directory upload. Having difficulties to find any example. Anyone know how to get File instance of each file in directory?
Copied from that article:
<div id=”dropzone”></div>
var dropzone = document.getElementById('dropzone');
dropzone.ondrop = function(e) {
var length = e.dataTransfer.items.length;
for (var i = 0; i < length; i++) {
var entry = e.dataTransfer.items[i].webkitGetAsEntry();
if (entry.isFile) {
... // do whatever you want
} else if (entry.isDirectory) {
... // do whatever you want
}
}
};
Use DirectoryReader directoryEntry.createReader() , readEntries() for folders or , FileEntry file() for single or multiple file drops.
html
<div id="dropzone"
ondragenter="event.stopPropagation(); event.preventDefault();"
ondragover="event.stopPropagation(); event.preventDefault();"
ondrop="event.stopPropagation(); event.preventDefault(); handleDrop(event);">
Drop files
</div>
javascript
function handleFiles(file) {
console.log(file);
// do stuff with `File` having `type` including `image`
if (/image/.test(file.type)) {
var img = new Image;
img.onload = function() {
var figure = document.createElement("figure");
var figcaption = document.createElement("figcaption");
figcaption.innerHTML = file.name;
figure.appendChild(figcaption);
figure.appendChild(this);
document.body.appendChild(figure);
URL.revokeObjectURL(url);
}
var url = URL.createObjectURL(file);
img.src = url;
} else {
console.log(file.type)
}
}
function handleDrop(event) {
var dt = event.dataTransfer;
var files = dt.files;
var length = event.dataTransfer.items.length;
for (var i = 0; i < length; i++) {
var entry = dt.items[i].webkitGetAsEntry();
if (entry.isFile) {
// do whatever you want
console.log("isFile", entry.isFile);
entry.file(handleFiles);
} else if (entry.isDirectory) {
// do whatever you want
console.log("isDirectory", entry.isDirectory);
var reader = entry.createReader();
reader.readEntries(function(entries) {
entries.forEach(function(dir, key) {
dir.file(handleFiles);
})
})
}
}
}
plnkr http://plnkr.co/edit/eGAnbA?p=preview
After you drag some file from your disk. This event.dataTransfer.file is your fileList object.
Your could create a formData then
Add files from fileList to formData one by one.
In the end you could submit formData to server with Ajax

Send default image with ajax

Hi guys how can i convert a image to object in this code for example:
var img = $('<img id="dynamic">');
img.attr('src', 'http://macreationdentreprise.fr/wp-content/uploads/2012/06/ouvrir-un-restaurant.jpeg');
var obj=img.ConvertToObject();
// so i can now use obj.filename; and obj.data;
The reason to do that is if i wan't to upload this image automatically if the input file isn't set:
<input id="picture_zone_upload_input_ID" type="file" name="picture_zone_upload_input" class="picture_zone_upload_input" multiple title="Choose a file to upload"/>
EDIT
function configure_zone_upload() {
$("#picture_zone_upload_input_ID").change(function (event) {
$.each(event.target.files, function (index, file) {
var reader = new FileReader();
reader.onload = function (event) {
object = {};
object.filename = file.name;
object.data = event.target.result;
//alert("index: " + index);
upload_img_count++;
configure_upload_img(object.data, upload_img_count);
files.push(object);
};
reader.readAsDataURL(file);
});
var files = event.target.files;
for (var x = 0; x < files.length; x++) {
data.append(x, files[x]);
}
//alert(files.length);
AJAX_upload_Profile(data, upload_img_count);
});
}
If there isn't a input file how can i set the my default image to send it with ajax ??
EDIT2
If event.target.files; is null how can i set file from new image:
var files = event.target.files;
for (var x = 0; x < files.length; x++) {
data.append(x, files[x]);
}
//alert(files.length);
AJAX_upload_Profile(data, upload_img_count);
I guess you can do this:
object.filename = file.name || defaultImg;
where defaultImg is a variable which contains a default img with src.
somthing like:
reader.onload = function (event) {
object = {};
object.defaultImg = $('<img>',{"src" : "defaultImg/path/img.jpeg"}); // <--declare here
object.filename = file.name
object.data = event.target.result || object.defaultImg; //<---use it here;
//alert("index: " + index);
upload_img_count++;
configure_upload_img(object.data, upload_img_count);
files.push(object);
};

Categories