flickr style image upload using html5 canvas - javascript

i am trying to make an image upload gallery using html5 canvas . I am trying to resize the images at the client side and show the preview . but when i upload multiple images with big size like 5mb each , the browser halts and sometimes crashes . I checked on flickr so their system instantly resize the uploaded images without any load to browser no matter how many pictures i upload . moreover my thumbnails when resized give the poor quality and whatever if i do something to make a make it better , the load on my browser shoots up . Here is my code for the images preview and resize
$(document).ready(function() {
if (window.File && window.FileReader && window.FileList && window.Blob) {
document.getElementById('getfiles').onchange = function(){
var files = document.getElementById('getfiles').files;
for(var i = 0; i < files.length; i++) {
resizeAndUpload(files[i]);
}
};
} else {
alert('The File APIs are not fully supported in this browser.');
}
function resizeAndUpload(file) {
var reader = new FileReader();
reader.onloadend = function() {
var tempImg = new Image();
tempImg.src = reader.result;
tempImg.onload = function() {
var MAX_WIDTH = 220;
var MAX_HEIGHT = 120;
var tempW = tempImg.width;
var tempH = tempImg.height;
if (tempW > tempH) {
if (tempW > MAX_WIDTH) {
tempH *= MAX_WIDTH / tempW;
tempW = MAX_WIDTH;
}
} else {
if (tempH > MAX_HEIGHT) {
tempW *= MAX_HEIGHT / tempH;
tempH = MAX_HEIGHT;
}
}
var canvas = document.createElement('canvas');
canvas.width = tempW;
canvas.height = tempH;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0, tempW, tempH);
var dataURL = canvas.toDataURL("image/jpeg");
var newImgs = new Image();
newImgs.src = dataURL;
document.body.appendChild(newImgs);
}
}
reader.readAsDataURL(file);
}

For the "browser halts and sometimes crashes" part of your troubles...
Make sure you give the images time to load using .onload:
var newImgs=new Image();
newImgs.onload=function(){
document.body.appendChild(newImgs);
}
newImgs.src=dataURL;
And note that the .onload should be declared before .src
So, put .onload before .src on your tempImg also.
var tempImg = new Image();
tempImg.onload = function() {
...
}
tempImg.src = reader.result;
As far as the poor quality when up-sizing thumbnails, that's unavoidable.
You thumbnail only has a small pixel resolution and up-sizing will always create poor results.

Related

Upload Image, Resize, Modify, Download with JavaScript

For the most part this is working, except the uploaded image resizes back to its original size within the canvas when text is added. I'll post a couple of example images below for a visual.
Here is the JavaScript:
function upImage(imageFile) {
let imgInput = document.getElementById('imageIn');
imgInput.addEventListener('change', function(e) {
if(e.target.files) {
let imageFile = e.target.files[0]; //here we get the image file
var reader = new FileReader();
reader.readAsDataURL(imageFile);
reader.onloadend = function (e) {
gImgObj = new Image(); // Creates image object
gImgObj.src = e.target.result; // Assigns converted image to image object
gImgObj.onload = function(ev) {
var myCanvas = document.querySelector('.memeCanvas'); // Creates a canvas object
var myContext = myCanvas.getContext("2d"); // Creates a contect object
var MAX_WIDTH = 800;
var MAX_HEIGHT = 600;
var width = gImgObj.width;
var height = gImgObj.height;
if (width > height) {
if (width > MAX_WIDTH) {
height *= MAX_WIDTH / width;
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width *= MAX_HEIGHT / height;
height = MAX_HEIGHT;
}
}
myCanvas.width = width;
myCanvas.height = height;
myContext.drawImage(gImgObj, 0, 0, width, height);
}
}
}
});
}
Here is the HTML:
<input id="imageIn" type="file" accept=".jpg,.jpeg,.png" onclick="upImage(this)" onchange="runMemeEditor()"/>
Here is what the initial uploaded image looks like:
Here is what happens when the editor overlay changes:
The call to runMemeEditor() simply loads a few functions which displays a Meme Generator. Like I said, it technically works as designed, but the uploaded image when larger than 800x600 end up resizing back to its original size within the canvas.
Thanks for looking.

Scaling image from input[type=file]

Desired Result: I'm looking to select an image from a file upload form, scale it to a thumbnail and display it.
Problem: The following code does exactly what I want it to, however, I must select the file not once, but twice to see the image preview. (Select image, no display, select same image, I get the scaled display) Everything was working great when I was manually assigning width & height, though now that i'm scaling it - this issue began. I'm in need of a code review! When I comment out the if/if else / else statement and manually assign img.width & img.height to be 75 each, I get the display though it's of course not scaled.
previewFiles = function (file, i) {
preview = function (file) {
// Make sure `file.name` matches our extensions criteria
switch (/\.(jpe?g|png|gif)$/i.test(file.name)) {
case true:
var reader = new FileReader();
reader.onload = function (e) {
var img = new Image();
img.src = reader.result;
var width = img.width,
height = img.height,
max_size = 75;
if (width <= max_size && height <= max_size) {
var ratio = 1;
} else if (width > height) {
var ratio = max_size / width;
} else {
var ratio = max_size / height;
}
img.width = Math.round(width * ratio);
img.height = Math.round(height * ratio);
img.title = file.type;
$('div.box.box-primary').find('span.prev').eq(i).append(img);
};
reader.readAsDataURL(file);
break;
default:
$('div.box.box-primary').find('span.prev').eq(i).append('<a class="btn btn-app" href="#"><span class="vl vl-bell-o"></span> Unsupported File</a>');
break;
}
};
preview(file);
};
I have changing the scaling up a bit - tried https://hacks.mozilla.org/2011/01/how-to-develop-a-html5-image-uploader/ following this article and I have the same issue. Is the problem due to the fact that i'm not using a canvas? I'm pretty new w/jQuery & javascript - Any help here is greatly appreciated!
I made this snippet that fetches an image, thumbnails it & exports it as an img element.
// limit the image to 150x100 maximum size
var maxW=150;
var maxH=100;
var input = document.getElementById('input');
input.addEventListener('change', handleFiles);
function handleFiles(e) {
var img = new Image;
img.onload = function() {
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
var iw=img.width;
var ih=img.height;
var scale=Math.min((maxW/iw),(maxH/ih));
var iwScaled=iw*scale;
var ihScaled=ih*scale;
canvas.width=iwScaled;
canvas.height=ihScaled;
ctx.drawImage(img,0,0,iwScaled,ihScaled);
var thumb=new Image();
thumb.src=canvas.toDataURL();
document.body.appendChild(thumb);
}
img.src = URL.createObjectURL(e.target.files[0]);
}
body{ background-color: ivory; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Select a file to create a thumbnail from</h4>
<input type="file" id="input"/>
<br>

Multiple image upload & resize freezes chrome or even crashes it

I have a Javascript that allows to resize multiple images and uploads them. So far it works until you choose more than about 5 files. From about 5 to 25 selected files Chrome browser (or others) just gets really slow, if you choose even more the browser crashes.
It seems to take a lot of memory.
Do you have any suggestions what could avoid the freezing of the browser/computer or the crash?
Thank you very much for your help!
// Once files have been selected
document.querySelector('form input[type=file]').addEventListener('change', function(event){
$( "#load" ).html( "<img src=\"http://www.maxk.at/image_uploader/ajax-loader.gif\"> Upload läuft" );
// Read files
var files = event.target.files;
// Iterate through files
var j=1;
var k=files.length;
for (var i = 0; i < files.length; i++) {
// Ensure it's an image
if (files[i].type.match(/image.*/)) {
// Load image
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {
// Add elemnt to page
var imageElement = document.createElement('div');
imageElement.classList.add('uploading');
imageElement.innerHTML = '<span class="progress"><span></span></span>';
var progressElement = imageElement.querySelector('span.progress span');
progressElement.style.width = 0;
document.querySelector('form div.photos').appendChild(imageElement);
// Resize image
var canvas = document.createElement('canvas'),
max_size = 1200,
width = image.width,
height = image.height;
if (width > height) {
if (width > max_size) {
height *= max_size / width;
width = max_size;
}
} else {
if (height > max_size) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
// Upload image
var xhr = new XMLHttpRequest();
if (xhr.upload) {
// Update progress
xhr.upload.addEventListener('progress', function(event) {
var percent = parseInt(event.loaded / event.total * 100);
progressElement.style.width = percent+'%';
}, false);
// File uploaded / failed
xhr.onreadystatechange = function(event) {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
imageElement.classList.remove('uploading');
imageElement.classList.add('uploaded');
imageElement.style.backgroundImage = 'url('+xhr.responseText+')';
console.log('Image uploaded: '+j+k+'Abstand'+xhr.responseText);
j++;
if (j==k){$("#load").html("Upload beendet");}
if (k==1){$("#load").html("Upload beendet");}
} else {
imageElement.parentNode.removeChild(imageElement);
}
}
}
// Start upload
xhr.open('post', 'process.php', true);
xhr.send(canvas.toDataURL('image/jpeg'));
}
}
image.src = readerEvent.target.result;
}
reader.readAsDataURL(files[i]);
}
}
// Clear files
event.target.value = '';
});

Javascript file resizing generating bad base64 string

I've been trying to resize images client side (using HTML5) before upload. It resizes it and sends through a base64 string, but it seems to be broken. The base64 string has spaces and line breaks and weird things, even if I take them out, it seems to still be broken.
This is my HTML:
<div class="form-group">
<canvas id="canvas"></canvas>
<label for="fileToUpload">Select Files to Upload</label>
<input type="file" id="FileUpload1" name="FileUpload1" />
<output id="filesInfo"></output>
</div>
And of course my javascript:
<script type="text/javascript">
if (window.File && window.FileReader && window.FileList && window.Blob) {
document.getElementById('FileUpload1').onchange = function () {
var files = document.getElementById('FileUpload1').files;
for (var i = 0; i < files.length; i++) {
resizeAndUpload(files[i]);
}
};
} else {
alert('The File APIs are not fully supported in this browser.');
}
function resizeAndUpload(file) {
var reader = new FileReader();
reader.onloadend = function () {
var tempImg = new Image();
tempImg.src = reader.result;
tempImg.onload = function () {
var MAX_WIDTH = 400;
var MAX_HEIGHT = 300;
var tempW = tempImg.width;
var tempH = tempImg.height;
if (tempW > tempH) {
if (tempW > MAX_WIDTH) {
tempH *= MAX_WIDTH / tempW;
tempW = MAX_WIDTH;
}
} else {
if (tempH > MAX_HEIGHT) {
tempW *= MAX_HEIGHT / tempH;
tempH = MAX_HEIGHT;
}
}
var canvas = document.getElementById('canvas');
canvas.width = tempW;
canvas.height = tempH;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0, tempW, tempH);
var dataURL = canvas.toDataURL("image/jpeg");
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function (ev) {
document.getElementById('filesInfo').innerHTML = 'Done!';
};
xhr.open('POST', 'Upload', true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
alert(dataURL);
var data = 'image=' + dataURL;
xhr.send(data);
}
}
reader.readAsDataURL(file);
}
</script>
The dataURL variable, that's what contains the bad base64 string.
Here is your problem:
Base 64 encoded string includes characters like / and + , which as you can imagine have special significance if the request content type is said to be application/x-www-form-urlencoded. So, with your current code, + sign gets decoded as a space, and combinations like \n get interpreted as control characters such as line feed, new line etc.
All you have to do is encode dataURL to make sure it is safe for use as a parameter value. This is as simple as using the encodeURIComponent method.
i.e. var data = 'image=' + encodeURIComponent(dataURL);
Hope this helps.

Image resize before upload

I need to provide a means for a user to upload photos to their web site in jpeg format. However, the photos are very large in original size, and I would like to make the resize before upload option very effortless for the user. It seems my only options are a client side application that resizes the photos before uploading them via a web service, or a client side JavaScript hook on the upload operation that resizes the images. The second option is very tentative because I don't have a JavaScript image resizing library, and it will be difficult to get the JavaScript to run my current resize tool, ImageMagick.
I'm sure this is not too uncommon a scenario, and some suggestions or pointers to sites that do this will be appreciated.
In 2011, we can know do it with the File API, and canvas.
This works for now only in firefox and chrome.
Here is an example :
var file = YOUR_FILE,
fileType = file.type,
reader = new FileReader();
reader.onloadend = function() {
var image = new Image();
image.src = reader.result;
image.onload = function() {
var maxWidth = 960,
maxHeight = 960,
imageWidth = image.width,
imageHeight = image.height;
if (imageWidth > imageHeight) {
if (imageWidth > maxWidth) {
imageHeight *= maxWidth / imageWidth;
imageWidth = maxWidth;
}
}
else {
if (imageHeight > maxHeight) {
imageWidth *= maxHeight / imageHeight;
imageHeight = maxHeight;
}
}
var canvas = document.createElement('canvas');
canvas.width = imageWidth;
canvas.height = imageHeight;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0, imageWidth, imageHeight);
// The resized file ready for upload
var finalFile = canvas.toDataURL(fileType);
}
}
reader.readAsDataURL(file);
There is multiple-technology-capable Plupload tool which declares that it can do resizing before upload, but I haven't tried it yet. I have also find a suitable answer in my question about binary image handling javascript libs.
You have several options:
Java
ActiveX (only on windows)
Silverlight
Flash
Flex
Google Gears (the most recent version is capable of resizing and drag and drop from your desktop)
I've done a lot of research looking for a similar solution to what you have described and there a lot of solutions out there that vary a lot in quality and flexibility.
My suggestion is find a solution which will do 80% of what you need and customize it to suit your needs.
I think you need Java or ActiveX for that. For example Thin Image Upload
What jao and russau say is true. The reason being is JavaScript does not have access to the local filesystem due to security reasons. If JavaScript could "see" your image files, it could see any file, and that is dangerous.
You need an application-level control to be able to do this, and that means Flash, Java or Active-X.
Unfortunately you won't be able to resize the images in Javascript. It is possible in Silverlight 2 tho.
If you want to buy something already done: Aurigma Image Uploader is pretty impressive - $USD250 for the ActiveX and Java versions. There's some demos on the site, I'm pretty sure facebook use the same control.
Here some modifications to feed tensorflow.js(soo fast with it!!) with resized and cropped image (256x256px), plus showing original image under cropped image, to see what is cut off.
$("#image-selector").change(function(){
var file = $("#image-selector").prop('files')[0];
var maxSize = 256; // well now its minsize
var reader = new FileReader();
var image = new Image();
var canvas = document.createElement('canvas');
var canvas2 = document.createElement('canvas');
var dataURItoBlob = function (dataURI) {
var bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ?
atob(dataURI.split(',')[1]) :
unescape(dataURI.split(',')[1]);
var mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
var max = bytes.length;
var ia = new Uint8Array(max);
for (var i = 0; i < max; i++)
ia[i] = bytes.charCodeAt(i);
return new Blob([ia], { type: mime });
};
var resize = function () {
var width = image.width;
var height = image.height;
if (width > height) {
if (width > maxSize) {
width *= maxSize / height;
height = maxSize;
}
} else {
if (height > maxSize) {
height *= maxSize / width;
width = maxSize;
}
}
if (width==height) { width = 256; height = 256; }
var posiw = 0;
var posih = 0;
if (width > height) {posiw = (width-height)/2; }
if (height > width) {posih = ((height - width) / 2);}
canvas.width = 256;
canvas.height = 256;
canvas2.width = width;
canvas2.height = height;
console.log('iw:'+image.width+' ih:'+image.height+' w:'+width+' h:'+height+' posiw:'+posiw+' posih:'+posih);
canvas.getContext('2d').drawImage(image, (-1)*posiw, (-1)*posih, width, height);
canvas2.getContext('2d').drawImage(image, 0, 0, width, height);
var dataUrl = canvas.toDataURL('image/jpeg');
var dataUrl2 = canvas2.toDataURL('image/jpeg');
if ($("#selected-image").attr("src")) {
$("#imgspeicher").append('<div style="width:100%; border-radius: 5px; background-color: #eee; margin-top:10px;"><div style="position: relative; margin:10px auto;"><img id="selected-image6" src="'+$("#selected-image").attr("src")+'" style="margin: '+document.getElementById('selected-image').style.margin+';position: absolute; z-index: 999;" width="" height=""><img id="selected-image2" src="'+$("#selected-image2").attr("src")+'" style="margin: 10px; opacity: 0.4;"></div><div class="row" style="margin:10px auto; text-align: left;"> <ol>'+$("#prediction-list").html()+'</ol> </div></div>');
}
$("#selected-image").attr("src",dataUrl);
$("#selected-image").width(256);
$("#selected-image").height(256);
$("#selected-image").css('margin-top',posih+10+'px');
$("#selected-image").css('margin-left',posiw+10+'px');
$("#selected-image2").attr("src",dataUrl2);
$("#prediction-list").empty();
console.log("Image was loaded, resized and cropped");
return dataURItoBlob(dataUrl);
};
return new Promise(function (ok, no) {
reader.onload = function (readerEvent) {
image.onload = function () { return ok(resize()); };
image.src = readerEvent.target.result;
};
let file = $("#image-selector").prop('files')[0];
reader.readAsDataURL(file);});});
Html implementation:
<input id ="image-selector" class="form-control border-0" type="file">
<div style="position: relative; margin:10px auto; width:100%;" id="imgnow">
<img id="selected-image" src="" style="margin: 10px; position: absolute; z-index: 999;">
<img id="selected-image2" src="" style="margin: 10px; opacity: 0.4;">
</div>
Also not resize to a maximum width/height, but to minimum. We get a 256x256px square image.
Pure JavaScript solution. My code resizes JPEG by bilinear interpolation, and it doesn't lose exif.
https://github.com/hMatoba/JavaScript-MinifyJpegAsync
function post(data) {
var req = new XMLHttpRequest();
req.open("POST", "/jpeg", false);
req.setRequestHeader('Content-Type', 'image/jpeg');
req.send(data.buffer);
}
function handleFileSelect(evt) {
var files = evt.target.files;
for (var i = 0, f; f = files[i]; i++){
var reader = new FileReader();
reader.onloadend = function(e){
MinifyJpegAsync.minify(e.target.result, 1280, post);
};
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
You can resize the image in the client-side before uploading it using an image processing framework.
Below I used MarvinJ to create a runnable code based on the example in the following page:
"Processing images in client-side before uploading it to a server"
Basically I use the method Marvin.scale(...) to resize the image. Then, I upload the image as a blob (using the method image.toBlob()). The server answers back providing a URL of the received image.
/***********************************************
* GLOBAL VARS
**********************************************/
var image = new MarvinImage();
/***********************************************
* FILE CHOOSER AND UPLOAD
**********************************************/
$('#fileUpload').change(function (event) {
form = new FormData();
form.append('name', event.target.files[0].name);
reader = new FileReader();
reader.readAsDataURL(event.target.files[0]);
reader.onload = function(){
image.load(reader.result, imageLoaded);
};
});
function resizeAndSendToServer(){
$("#divServerResponse").html("uploading...");
$.ajax({
method: 'POST',
url: 'https://www.marvinj.org/backoffice/imageUpload.php',
data: form,
enctype: 'multipart/form-data',
contentType: false,
processData: false,
success: function (resp) {
$("#divServerResponse").html("SERVER RESPONSE (NEW IMAGE):<br/><img src='"+resp+"' style='max-width:400px'></img>");
},
error: function (data) {
console.log("error:"+error);
console.log(data);
},
});
};
/***********************************************
* IMAGE MANIPULATION
**********************************************/
function imageLoaded(){
Marvin.scale(image.clone(), image, 120);
form.append("blob", image.toBlob());
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.marvinj.org/releases/marvinj-0.8.js"></script>
<form id="form" action='/backoffice/imageUpload.php' style='margin:auto;' method='post' enctype='multipart/form-data'>
<input type='file' id='fileUpload' class='upload' name='userfile'/>
</form><br/>
<button type="button" onclick="resizeAndSendToServer()">Resize and Send to Server</button><br/><br/>
<div id="divServerResponse">
</div>

Categories