I am trying to make an upload script which resizes multiple images client side before the saveimage.php handles them. The reason for this is because they will be uploaded at an outside location where there might be very slow internet.
I have been able to find pieces of code around here which helped me put it together. I am only a beginner so it's probably very messy!
What it currently does is it checks whenever files are being input in the 'file_input' field. Then it looks at the amount of files and loop through them until every file has been placed in the canvas and upload while it's in there.
However, the problem is: when I don't add an alert for each file, the loop goes too fast and only processes 1 or 2 out of 10 images for example. Because the upload script will go faster than the canvas can be updated with a new image.
This is my current page, simplified to the core:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
</head>
<body>
<form>
<input id="file_input" type='file' multiple />
</form>
<div id="preview">
<canvas id="canvas"></canvas>
</div>
<script>
var input = document.getElementById('file_input');
input.addEventListener('change', handleFiles);
function handleFiles(e) {
var files = input.files;
alert(files.length +" files are being uploaded!"); // display amount of files for testing purpose
for (var i = 0; i < files.length; ++i) {
alert("Upload file number: "+i); // display file # for testing purpose, when this is removed the script will go too fast
var MAX_HEIGHT = 1000;
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image;
img.onload = function(){
if(img.height > MAX_HEIGHT) {
img.width *= MAX_HEIGHT / img.height;
img.height = MAX_HEIGHT;
}
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
preview.style.width = img.width + "px";
preview.style.height = img.height + "px";
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
// the canvas should contain an image now and this will send it through saveimage.php
var myDrawing = document.getElementById("canvas");
var drawingString = myDrawing.toDataURL("image/jpeg", 0.5);
var postData = "canvasData="+drawingString;
var ajax = new XMLHttpRequest();
ajax.open("POST",'saveimage.php',true);
ajax.setRequestHeader('Content-Type', 'canvas/upload');
ajax.onreadystatechange=function()
{
if (ajax.readyState == 4);
}
ajax.send(postData);
};
img.src = URL.createObjectURL(e.target.files[i]);
}
}
</script>
</body>
</html>
And this is my saveimage.php which works well, but just for the complete overview:
<?php
if (isset($GLOBALS["HTTP_RAW_POST_DATA"]))
{
$rawImage=$GLOBALS['HTTP_RAW_POST_DATA'];
$removeHeaders=substr($rawImage, strpos($rawImage, ",")+1);
$decode=base64_decode($removeHeaders);
// check if the file already exists and keep adding +1 intil it's unique
$i = 1;
while(file_exists('uploads/image'.$i.'.jpg')) {
$i++;
}
$fopen = fopen( 'uploads/image'.$i.'.jpg', 'wb' );
fwrite( $fopen, $decode);
fclose( $fopen );
}
?>
A bit of extra information:
I have added canvas, because as far as I can understand that's the
only way to resize images client side. In the final script it will be
set to hidden.
If the canvas could be skipped and the data sent to saveimage.php
immediately then that would solve the problem too I think.
As I said I am not very experienced with javascript, so if there is a
much simpler way to achieve this goal. Please let me know!
Thanks in advance!
JavaScript loads image asynchronously. That means once it is assigned a new image's .src it will begin loading the image but will simultaneously continue processing the javascript after the .src while the image takes time to load.
You're using the same image variable (var img) inside your for loop. Therefore, each time through the loop you are overwriting the previous img before the previous image has been fully loaded.
Here's an image loader that fully loads all image and then calls the start() function where you can do you processing:
// image loader
// put the paths to your images in imageURLs[]
var imageURLs=[];
imageURLs.push("myImage1.png");
imageURLs.push("myImage2.png");
// etc, etc.
// the loaded images will be placed in imgs[]
var imgs=[];
var imagesOK=0;
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
}
}
function start(){
// the imgs[] array now holds fully loaded images
// the imgs[] are in the same order as imageURLs[]
}
In the onload callback use the this keyword(instead of img) to for the current image, img may/will be overwritten in the for loop before the callback fires.
img.height --> this.height etc
Also is there a reason you're using a custom content type instead of application/x-www-form-urlencoded ?
Url encoded form data is much easier to work with.
I have struggled with the same problem for some time now, but I finally
cobbled together another solution, using bits of code from others, who have addressed this problem,
and it works. It may not be perfect, and I would welcome any improvement suggestions.
The concept is that you have two Javascript
Functions, which are identicle called Resize0 and Resize1, and they call each other
until all photos have been resized then the Function which resizes the
last image Submits the Form. The two Functions pass parameters between
each other so that one of them will know when to Submit the Form.
Two of those parameters are, Number_Of_Images and current Image_Number.
You can also download a working Example Web page from
http://www.wantitconsulting.co.nz/ExampleResizeUpload.zip .
You will need to create a folder in the base directory of your test web site
for the Images to go into. The foldername is TestResizeUpload. The Server
side uses php.
Related
I've looked through and tried many solutions to this issue and nothing has worked. I have an image on a website that needs to be updated 10-30x a second (live video feed) so I have the javascript request the image every 100ms. When the image stays the same, no flickering. When the image changes, I see flickering on the image for 2-3 seconds.
function initImg() {
var canvas = document.getElementById("diagimg");
var context = canvas.getContext("2d");
img = new Image();
img.onload = function() {
var scale = .73;
canvas.setAttribute("width", 640*scale);
canvas.setAttribute("height", 480*scale);
context.scale(scale, scale); //scale it to correct size
context.drawImage(this, 0, 0);
}
img.onerror = function() {
img.src="images/wait.jpeg"; //if error during loading, display this image
}
refreshImg();
}
function refreshImg() {
img.src = "images/IMAGE.png?time="+new Date().getTime();
window.setTimeout("refreshImg()", 100);
}
initImg();
I've turned your code into an example to test this behaviour, but I don't see any flickering at all.
Is it possible that the flickering is caused by server-side code?
let images = [
'https://upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Mercury_in_color_-_Prockter07-edit1.jpg/220px-Mercury_in_color_-_Prockter07-edit1.jpg',
'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/PIA23791-Venus-NewlyProcessedView-20200608.jpg/220px-PIA23791-Venus-NewlyProcessedView-20200608.jpg',
'https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/The_Earth_seen_from_Apollo_17.jpg/220px-The_Earth_seen_from_Apollo_17.jpg',
'https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/OSIRIS_Mars_true_color.jpg/220px-OSIRIS_Mars_true_color.jpg'
];
let i = 0;
function getImage() {
i++;
if (i >= images.length)
i = 0;
return images[i];
}
//--------------------------------------
function initImg() {
var canvas = document.getElementById("diagimg");
var context = canvas.getContext("2d");
img = new Image();
img.onload = function() {
var scale = .73;
canvas.setAttribute("width", 640 * scale);
canvas.setAttribute("height", 480 * scale);
context.scale(scale, scale); //scale it to correct size
context.drawImage(this, 0, 0);
}
refreshImg();
}
function refreshImg() {
img.src = getImage() + "?time=" + new Date().getTime();
window.setTimeout("refreshImg()", 500);
}
initImg();
<canvas id="diagimg" />
Fixed the problem - turned out to be that I was trying to write an image at the same time that it was being read.
Quick summary of my setup: websocket connection between website and java program, the image being loaded onto the webpage is constantly being overwritten by an external program.
The fix was to have the website request the websocket server to copy the image. The server (java program) copies the image, checks if the copy is equal to the original, and sends a message to the website that the image is ready to be read. The device ID is also appended to the filepath so that each connected device (each instance of the website open) has its own image that will only be changed when it requests an update (it requests a new image once it's done loading).
This means that images are only overwritten when the client requests them, and the client only reads them when the java websocket says that it's done being copied.
I'm sure it's inefficient but it only needs to refresh at 10hz and the entire process only takes about 10ms on its own thread, so doesn't really matter.
I'm developing a printing tool using HTML5 canvas. As a test, I've tried to draw an image and a rectangle on the canvas, and then copy it to a new window for printing, using the code below. But all I'm getting in the new window is a blank page.
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<canvas id="pageCanvas"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("pageCanvas");
canvas.height="700";
canvas.width="1000";
var ctx = canvas.getContext("2d");
var imageData="data:image/png;base64,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";
var imageObj = new Image();
imageObj.src = imageData;
imageObj.addEventListener("load", function() {
ctx.drawImage(imageObj, 50, 100,1000,600);
ctx.beginPath();
ctx.rect(100, 101, 200, 100);
ctx.lineWidth = 7;
ctx.strokeStyle = 'black';
ctx.stroke();
}, false);
var printWindow = window.open('');
printWindow.document.write('<html><BODY>');
printWindow.document.write('<center>');
printWindow.document.write('<img src="' + canvas.toDataURL()+'"/>');
printWindow.document.write('</center></body></html>');
printWindow.document.close();
printWindow.print();
</script>
</body>
</html>
Can you please guide me to overcome this issue?
You're opening the new window and trying to copy the content of the canvas onto it immediately after attaching the load event handler to the image, before the handler has actually had a chance to execute.
Just move all the JS code starting with the var printWindow = window.open(''); line inside the event handler, and it should work.
Oh, and please indent your code, especially if you expect anyone else to read it.
Addendum: If you want to wait until multiple images have loaded, the simplest way is to count the load events and call a function when all of them have fired, like this:
var imageURLs = [ ... image URLs here ... ];
var imageObjs = [];
var imagesLoaded = 0;
for (var i = 0; i < imageURLs.length; i++) {
var image = new Image();
image.addEventListener( "load", function() {
imagesLoaded++;
if (imagesLoaded == imageURLs.length) allImagesLoaded();
} );
image.src = imageURLs[i];
imageObjs.push(image);
}
function allImagesLoaded() {
// now do something with imageObjs
};
You could also get fancy and play with things like ES6 promises, but ultimately, the end result is the same.
See also:
Can I sync up multiple image onload calls?
Javascript - wait images to be loaded
I'm having an issue while using canvas in a background page to create data URLs for desktop notifications' images.
I want to use the "image" notifications which require a 3:2 ratio to display properly. The images I want to use (from hulu.com) are a different ratio, so I decided to use the canvas element to create the corresponding data URL off of these images so that the ratio is correct. It kind of works in theory, but…
…I'm having issues if I'm creating more than one canvas/notification in the background page. One image is created properly, but the rest comes out empty.
Confusingly, opening the same background page in a new tab (i.e. exact same code) makes everything works just fine: all the notifications are created with the images loaded from hulu.com. Also, just changing the dimensions from 360x240 to 300x200 makes it work. Finally, though they're similar computers with the same Chrome version (34.0.1847.116), it works without modification at work while it doesn't on my own laptop.
I made a test extension available at the bottom of this post. Basically, it only has a generated background page. The code for that page is this:
var images = ["http://ib2.huluim.com/video/60376901?size=290x160&img=1",
"http://ib2.huluim.com/video/60366793?size=290x160&img=1",
"http://ib4.huluim.com/video/60372951?size=290x160&img=1",
"http://ib1.huluim.com/video/60365336?size=290x160&img=1",
"http://ib3.huluim.com/video/60376290?size=290x160&img=1",
"http://ib4.huluim.com/video/60377231?size=290x160&img=1",
"http://ib4.huluim.com/video/60312203?size=290x160&img=1",
"http://ib1.huluim.com/video/60376972?size=290x160&img=1",
"http://ib4.huluim.com/video/60376971?size=290x160&img=1",
"http://ib1.huluim.com/video/60376616?size=290x160&img=1"];
for (var i = 0; i < 10; i++) {
getDataURL(i);
}
/*
* Gets the data URL for an image URL
*/
function getDataURL(i) {
var img = new Image();
img.onload = function() {
var canvas = document.createElement('canvas');
canvas.width = 360;
canvas.height = 240;
var ctx = canvas.getContext('2d');
ctx.drawImage(this, 0, 0);
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
var dataURL = canvas.toDataURL('image/png');
chrome.notifications.create('', {
type: 'image',
iconUrl: 'logo_128x128.png',
title: String(i),
message: 'message',
imageUrl: dataURL
}, function(id) {});
}
//img.src = chrome.extension.getURL('logo_128x128.png');;
img.src = images[i];
}
The commented out line for img.src = ... is a test where it loads a local file instead of a remote one. In that case, all the images are created.
The red rectangle added to the canvas is to show that it's not just the remote image that is an issue: the whole resulting canvas is empty, without any red rectangle.
If you download and add the test extension below, you should get 10 notifications but only one with an image.
Then, to open the background page in a new tab, you can inspect the background page, type this in the console:
chrome.extension.getURL('_generated_background_page.html')
and right-click the URL, and click "Open in a new Tab" (or window). Once open you should get 10 notifications that look fine.
Any idea of what is going on? I haven't been able to find any kind of limitations for background pages relevant to that. Any help would be appreciated, because this has been driving me crazy!
Files available here: https://www.dropbox.com/s/ejbh6wq0qixb7a8/canvastest.zip
edit: based on #GameAlchemist's comment, I also tried the following: same getDataURL method, but the loop wrapped inside an onload for the logo:
function loop() {
for (var i = 0; i < 10; i++) {
getDataURL(i);
}
}
var logo = new Image();
logo.onload = function () {
loop();
}
logo.src = chrome.extension.getURL('logo_128x128.png');
Remember that the create() method is asynchronous and you should use a callback with. The callback can invoke next image fetching.
I would suggest doing this in two steps:
Load all the images first
Process the image queue
The reason is that you can utilize the asynchronous image loading better this way instead of chaining the callbacks which would force you to load one and one image.
For example:
Image loader
var urls = ["http://ib2.huluim.com/video/60376901?size=290x160&img=1",
"http://ib2.huluim.com/video/60366793?size=290x160&img=1",
"http://ib4.huluim.com/video/60372951?size=290x160&img=1",
"http://ib1.huluim.com/video/60365336?size=290x160&img=1",
"http://ib3.huluim.com/video/60376290?size=290x160&img=1",
"http://ib4.huluim.com/video/60377231?size=290x160&img=1",
"http://ib4.huluim.com/video/60312203?size=290x160&img=1",
"http://ib1.huluim.com/video/60376972?size=290x160&img=1",
"http://ib4.huluim.com/video/60376971?size=290x160&img=1",
"http://ib1.huluim.com/video/60376616?size=290x160&img=1"];
var images = [], // store image objects
count = urls.length; // for loader
for (var i = 0; i < urls.length; i++) {
var img = new Image; // create image
img.onload = loader; // share loader handler
img.src = urls[i]; // start loading
images.push(img); // push image object in array
}
function loader() {
count--;
if (count === 0) process(); // all loaded, start processing
}
//TODO need error handling here as well
Fiddle with concept code for loader
Processing
Now the processing can be isolated from the loading:
function process() {
// share a single canvas (use clearRect() later if needed)
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
current = 0;
canvas.width = 360;
canvas.height = 240;
createImage(); // invoke processing for first image
function createImage() {
ctx.drawImage(images[current], 0, 0); // draw current image
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
chrome.notifications.create('', {
type : 'image',
iconUrl : 'logo_128x128.png',
title : String(i),
message : 'message',
imageUrl: canvas.toDataURL() // png is default
},
function(id) { // use callback
current++; // next in queue
if (current < images.length) {
createImage(); // call again if more images
}
else {
done(); // we're done -> continue to done()
}
});
}
}
Disclaimer: I don't have a test environment to test Chrome extensions so typos/errors may be present.
Hope this helps!
I have the following code :
function createImage(source) {
var pastedImage = new Image();
pastedImage.onload = function() {
document.write('<br><br><br>Image: <img src="'+pastedImage.src+'" height="700" width="700"/>');
}
pastedImage.src = source;
}
Here I am displaying the image through html image tag which I wrote in document.write and provide appropriate height and width to image.
My question is can it possible to displaying image into the canvas instead of html img tag? So that I can drag and crop that image as I want?
But how can I display it in canvas?
Further I want to implement save that image using PHP but for now let me know about previous issue.
Try This
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image(); // Create new img element
img.onload = function(){
// execute drawImage statements here This is essential as it waits till image is loaded before drawing it.
ctx.drawImage(img , 0, 0);
};
img.src = 'myImage.png'; // Set source path
Make sure the image is hosted in same domain as your site. Read this for Javascript Security Restrictions Same Origin Policy.
E.g. If your site is http://example.com/
then the Image should be hosted on http://example.com/../myImage.png
if you try http://facebook.com/..image/ or something then it will throw security error.
Use
CanvasRenderingContext2D.drawImage.
function createImage(source) {
var ctx = document.getElementById('canvas').getContext('2d');
var pastedImage = new Image();
pastedImage.onload = function(){
ctx.drawImage(pastedImage, 0, 0);
};
pastedImage = source;
}
Also MDN seems to be have nice examples.
I've read about various kinds of ways getting image dimensions once an image has fully loaded, but would it be possible to get the dimensions of any image once it just started to load?
I haven't found much about this by searching (which makes me believe it's not possible), but the fact that a browser (in my case Firefox) shows the dimensions of any image I open up in a new tab right in the title after it just started loading the image gives me hope that there actually is a way and I just missed the right keywords to find it.
You are right that one can get image dimensions before it's fully loaded.
Here's a solution (demo):
var img = document.createElement('img');
img.src = 'some-image.jpg';
var poll = setInterval(function () {
if (img.naturalWidth) {
clearInterval(poll);
console.log(img.naturalWidth, img.naturalHeight);
}
}, 10);
img.onload = function () { console.log('Fully loaded'); }
The following code returns width/height as soon as it's available. For testing change abc123 in image source to any random string to prevent caching.
There is a JSFiddle Demo as well.
<div id="info"></div>
<img id="image" src="https://upload.wikimedia.org/wikipedia/commons/d/da/Island_Archway,_Great_Ocean_Rd,_Victoria,_Australia_-_Nov_08.jpg?abc123">
<script>
getImageSize($('#image'), function(width, height) {
$('#info').text(width + ',' + height);
});
function getImageSize(img, callback) {
var $img = $(img);
var wait = setInterval(function() {
var w = $img[0].naturalWidth,
h = $img[0].naturalHeight;
if (w && h) {
clearInterval(wait);
callback.apply(this, [w, h]);
}
}, 30);
}
</script>
One way is to use the HEAD request, which asks for HTTP Header of the response only. I know in HEAD responses, the size of the body is included. But I don't know if there anything available for size of images.