What would cause an HTML5 canvas toBlob to create an incomplete image? - javascript

I have the following javascript produce images from a canvas and upload them to a server.
var can = document.createElement('canvas');
can.width = 600;
can.height = 600;
var ctx = can.getContext('2d');
ctx.fillRect(0, 0, can.width, can.height);
ctx.fillText("Julia", can.width/2, can.height/2);
can.toBlob(uploadImage, "image/jpg", 0.9);
function uploadImage(jpeg) {
var data = new FormData();
data.append('image', jpeg, 'image.jpg');
...
}
Every so often, the result looks like the above, only partially drawn. Multiple canvases are processed and uploaded serially, only moving on in the completion of the ajax (in the ... part), so only one at a time.
Have you seen this happen? If so, where in this process should I debug further? Maybe a setting or something in the context object?
Edit
The upload is an ajax post with a promise resolved only on the success branch. It actually uses angular's $http service:
$http({method: 'POST', url: '...', data: data}).then(function(response) {
// callback that processes and uploads the next image
});

We were facing with similar except part with canvas. You can debug it simply.
Print given blob to page as img src to make sure that file was created properly. Print also blob size.
Open browser's developer tools -> Network -> Filter XHR and start uploading
Assign finished(status 200) ajax request and look at Request Headers -> Content-Length. If this number is equal to blob size on page, it was uploaded properly and find issue on server side. Else check ajax call.
Make sure that server has set max client request size appropriate to your usecase. For example nginx has default value 1M.
TIP: set Content-Type by blob.type in $http headers. Default value for POST is application/json.

try to rearrange your logic a little,, you are getting racing condition with uploading process and when the image is created, they have to be callback in callback sorta ..
// ...
ctx.fillText("Julia", can.width / 2, can.height / 2);
can.toBlob(function(blob) {
var newImg = document.createElement('img'),
url = URL.createObjectURL(blob);
newImg.onload = function() {
// no longer need to read the blob so it's revoked
URL.revokeObjectURL(url);
newImg.src = url;
// we have image out of canvas and now do the upload of newImg
// $http || $.ajax call goes here
};
});
of if you want to submit image data URL try something like,,
// ...
ctx.fillText("Julia", can.width / 2, can.height / 2);
can.toBlob(function(blob) {
var reader = new window.FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
base64data = reader.result;
// we have image in base64 and now do the upload of base64data
// $http || $.ajax call goes here
}
});
hth, k

Related

How to send an image to a server, from just the image url?

I am trying to send an image to a server for my chrome extension. I am using code from a previous discussion --> how to send image to server with http.post in javascript and store base64 in mongodb.
My problem is that the image is never actually uploaded to the page. For instance, I am trying to upload the image to this input on server that is not mine: image upload section. After the code runs the image section is not uploaded with an image, which is ultimate goal of my program.
This is what the terminal looks like after all the console.log()'s run: enter image description here
Does anyone know the reason behind this and how I could fix this? Simply put, I want the desired image to be uploaded to "Add Photos" sections.
If anyone has a better solution to how approach the entirety of the problem, please let me know!
var convertToBase64 = function(url, imagetype, callback){
var img = document.createElement('IMG'),
canvas = document.createElement('CANVAS'),
ctx = canvas.getContext('2d'),
data = '';
// Set the crossOrigin property of the image element to 'Anonymous',
// allowing us to load images from other domains so long as that domain
// has cross-origin headers properly set
img.crossOrigin = 'Anonymous'
// Because image loading is asynchronous, we define an event listening function that will be called when the image has been loaded
img.onload = function(){
console.log('hoiii');
// When the image is loaded, this function is called with the image object as its context or 'this' value
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
console.log(canvas);
console.log(ctx);
data = canvas.toDataURL(imagetype);
console.log(data);
callback(data);
};
// We set the source of the image tag to start loading its data. We define
// the event listener first, so that if the image has already been loaded
// on the page or is cached the event listener will still fire
img.src = url;
console.log(img);
};
// Here we define the function that will send the request to the server.
// It will accept the image name, and the base64 data as arguments
var sendBase64ToServer = function(name, base64){
var httpPost = new XMLHttpRequest(),
path = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAFmklEQVRYR+1YWUiVaxRdx1nLOh7NynMpDbOOA4Z5pdvgQNFDb3VJIknqoVIQExyTNBHRCppoEkW0yVKQsMT7IBZGEZoiRaSlpSYOWM6m5HhZW4x75db5f4en24aDcv7z/Xt9a++99v4+zdTU1BTmaa9fv8a1a9fw9OlTTE5OwtvbGwEBAVi6dCl27NiBdevWzdmDZj4A29racPLkSTx48ABDQ0Pw9fXFnj174O/vj9HRUTx8+BAvXrzA3r17ERsbCxsbG9VA5wywtbUV8fHxKCwsxMTEBHbt2oXTp09j+/bt30F8/foVO3fuRGVlJY4cOYKUlBSsWbNGFcg5ARwfH0dCQgLOnz8vzjZt2oTs7GxhcLa9ffsWHh4e8vXhw4eRm5u7+ABLSkoQFhYGhpiWnp4uof6RkdX379/D3t4eOTk52Lp1q2KQc2IwNDQUt2/flpxiEWRmZsLZ2fmHTo8fP466ujp8+fIFBw4cQHJy8uIBZM6Rvd7eXlhaWuLWrVsIDg7+qcNLly4hPz9fcnXDhg3yv1JTzWBISIg40Ol00Gg0wooxe/78uRQQjarGql+2bJmxZfJcFcBPnz7Bx8cH3d3dsphs7t+/36ijd+/e4ejRo7C2tpZNJSUlYdu2bUbXqQZYUFCAY8eOYWBgAHZ2dqJxDJkxa2hoQHh4OExNTTE2NiY5GBgYaGyZegYPHTqEu3fvSpjo4MmTJ4qcEGBUVJSIN7WRerh7925Fa1WF2MvLC2/evJEXnzlzRoRaibGC4+LihHl2nIyMjMUB6OLigubmZixZsgQvX76EwWBQgg81NTUi7Kx8ivzVq1f/1XF+9hJVDFLrWlpapNdWVFQoAscfPX78WNgmwOXLl4uGuru7K1qvCiClpa+vD6dOnUJqaqoiB/wRZSU6OhqDg4My2bATrVixQtF6VQApERTnvLw86QhKjbp54sQJjIyMyChWXl4OKysrRcsVA7x48SJu3LghIaZksDsotaSkZFy+fFkY5GTj5/e70qXKhZoD6YULF/Dx40eZWmJiYsCqNpZLk5MAuw9FndrJoUGn0y48QDogC+wmXV1dMDc3l4k5Pj4BBw+GYOXK/86p4uJHspnGxkZs3rwZ1dVVisGp6iRpaWm4f/++dANWIwfWadMISIo4da6srAzV1dUiyNwAWbtz544MChwqCgruLTxA5g3Zo/bR+vv78fnz5+8AV61aLeMWi4AzIiudWunk5CRdh+wRaGJiIqKiIhce4Llz5+R8wSqmQ7LX2dkpYV6/3g39/QPo6emRyiQoMse2RtaampoEkJubmwyrBoPx3v3PHSiq4sjISBQXF8uASmbYrvjhhGxubgGeCwmIz8gkpYjFROY4HHBjLKaKCmW9WzXAs2fPCoOUCTMzM3z79k3CTIBk0MrKWkJOVrVaLfR6vQDu6OjAhw8fZJ2rqytevapVFV7FRcJmf+XKFZSWloqDGZAM5ZYtf2BwcOj7IEAhnjleEiDX8rN27Vr5a2KiDqOiEDOXsrKyRJwp1Gx5HD6Zj3r9b9Dp7KWF8dBuYmIiAy0ZJSB+x5zloFtTM11kakwRQL6QRcDTG4EyZDSCMRjcodXawdPTU6qYIaXEsGAoR5ymbW1tERERgfT0NDXYpkVMzc0Cc+z69evS/Nvb22V0IoOjo2NwcHAQUBzHmJ8sIj5nwfCQxQHDwUG3uABn3l5fXy/jflVVFfbt+xOlpX/h5s2bwtiMkV3mKK88eKug169WDU41g7M9PHv2TAbP1tbpOxqG1sLCAhs3bpTLIx6MLC3N5wRsZpGqEM/2NDw8jLq6ejg7u6C2thZBQUEwNdVgfHwSZmYqy/UH25gXQL7z0aMS+Pn5gXcwQUHKTmpqKJ03wKKiIjg6OsqtFbVuoe0XwPky+ovBXwwaY+B/X8V/A+fXNLaIW65OAAAAAElFTkSuQmCC",
data = JSON.stringify({image: base64});
httpPost.onreadystatechange = function(err) {
if (httpPost.readyState == 4 && httpPost.status == 200){
console.log(httpPost.responseText);
} else {
console.log(err);
}
};
// Set the content type of the request to json since that's what's being sent
httpPost.open("POST", path, true);
httpPost.setRequestHeader('Content-Type', 'image/jpeg');
httpPost.send(data);
};
// This wrapper function will accept the name of the image, the url, and the
// image type and perform the request
var uploadImage = function(src, name, type){
convertToBase64(src, type, function(data){
sendBase64ToServer(name, data);
});
};
// Call the function with the provided values. The mime type could also be png
// or webp
uploadImage(result.products[pointer][3], 'cool.jpeg', 'image/jpeg');

Phonegap | converting imageuri to base64 | getting black image after uploading it to server using jquery

I want a valid string of base64 from imageuri. I have use below code for this but it's getting black image after uploading it to the server.
function encodeImageUri(imageUri)
{
var c=document.createElement('canvas');
var ctx=c.getContext("2d");
var img=new Image();
img.onload = function(){
c.width=this.width;
c.height=this.height;
ctx.drawImage(img, 0,0);
};
img.src=imageUri;
var dataURL = c.toDataURL("image/jpeg");
return dataURL;
}
I have also tried other options but not getting solid solution to get the valid base64 imagedata. I don't want to upload image using file-transfer method. I want to convert the imageuri in simple base64 string. Please suggest me specific answer for the same.
Thanks
Please note that I've not tested this within PhoneGap.
It's possible you're being caught out by the image being loaded asynchronously; you're encoding it into a string before it's actually loaded and processed in full.
There are various ways of handling this; one possibility is to move to using a success() method to handle the return value, which will be called once the image has loaded and been drawn:
function encodeImageUri(imageUri, success)
{
var c=document.createElement('canvas');
var ctx=c.getContext("2d");
var img=new Image();
img.onload = function(){
c.width=this.width;
c.height=this.height;
ctx.drawImage(img, 0,0);
success(c.toDataURL("image/jpeg"));
};
img.src=imageUri;
}
You will then need to call encodeImageUri with a function that will handle the end result, for example:
encodeImageUri(" < url > ", function (data) {
console.log(data);
});
Depending upon the security model PhoneGap uses, you may have to look out for CORS-related issues (see Tainted canvases may not be exported).

Unable to send blob payload in FormData

I'm playing around with the FormData API. Here is the fiddle I'm using - I'm trying to inject blobs into my form and submit via AJAX.
//ic0 and tc0 are canvases
//image0 and thumb0 are file inputs
function ajaxSubmit(){
var fd = new FormData(document.forms[0]);
ic0.toBlob(
function(blob){
fd.set("image0", blob, "image0.jpg");
}, "image/jpeg", 0.7);
tc0.toBlob(
function(blob){
fd.set("thumb0", blob, "thumb0.jpg");
}, "image/jpeg", 0.7);
var xhr = new XMLHttpRequest();
xhr.open("POST", "/ajax-upload/", true);
xhr.send(fd);
}
Browser behaviour is a little ... odd:
Chrome 50 (in Ubuntu)
Not able to do it, gives:
Failed blob:https%3A//fiddle.jshell.net/uuid4 to load resource: the server responded with 404
But I thought FormData.set() was supported now? It seems to work with non-blobs?
Firefox 46 (in Ubuntu)
Doesn't seem work if FormData() object is not initialized with a DOM object that already has the necessary file inputs (appended normal inputs are posted however). FormData.set() does not seem to work with file inputs and blobs (you'll note that thumb0 is null in the payload despite calling fd.set("thumb0", blob, "thumb0.jpg") on it. You can verify that it's set by doing console.log(fd.get("thumb0")) just after setting. You'll also note that the payload of image0 is incorrectly your original image, not the resized canvas image.
It's inconvenient not being able to customise your multipart FormData with javascript. I'm a javascript noob - am I just doing something completely wrong here, or are these browsers not correctly supporting the FormData API? How can I submit image0 and thumb0 correctly in my fiddle?
Edit: OK, going to bounty this. I know there are big jquery-dependent libraries like blueimp out there, and I think they base64 the data rather than transmitting as a file input (plus I want to avoid jquery). I only have to support the latest Chrome or Firefox for this project and I'm really hoping I can keep the code as clean as the FormData API seems to suggest I might be able to. Can anyone successfully grab an image off a canvas and insert it into the POST payload as a file? I'd like to be able to access it in request.FILES in django.
I think one thing you are missing here is the asynchronous nature of javascript. In your ajaxSubmit method -
function ajaxSubmit(){
var fd = new FormData(document.forms[0]);
ic0.toBlob(function(blob){
fd.set("image0", blob, "image0.jpg");
}, "image/jpeg", 0.7); // this call is asynchronous
tc0.toBlob(function(blob){
fd.set("thumb0", blob, "thumb0.jpg");
}, "image/jpeg", 0.7); // this call is also asynchronous
/*
hence the below code may get executed before
fd.set("image0", blob, "image0.jpg");
this statement. which it does.
that is why the canvas files are not submitted.
*/
var xhr = new XMLHttpRequest();
xhr.open("POST", "/ajax-upload/", true);
xhr.send(fd);
}
Try synchronising the code like this
function ajaxSubmit(){
var fd = new FormData(document.forms[0]);
ic0.toBlob(function(blob){
fd.set("image0", blob, "image0.jpg");
tc0.toBlob(function(blob){
fd.set("thumb0", blob, "thumb0.jpg");
console.log(blob);
console.log("Just blobbed thumb0");
var xhr = new XMLHttpRequest();
xhr.open("POST", "fileuploadbackend.jsp", true);
//xhr.setRequestHeader("X-CSRFToken", csrftoken);
xhr.send(fd);
}, "image/jpeg", 0.7);
}, "image/jpeg", 0.7);
}
also as jornare has suggested you should comment out the line
URL.revokeObjectURL(img.src);
which is causing error in chrome. Invoke it after the loading image is done.
also you have multiple elements with same id,
<input id="image0" name="image0" type="file" />
<label for="image0">image0</label>
<input id="image0" name="thumb0" type="file" />
this isn't creating problem in your code but the id's should be different.
UPDATE
Here is the working fiddle.
Note: change the request url to your corresponding url.
Follow these links to get more insight on asynchronous behaviour of javascript and how AJAX request callbacks are handled.
How does Asynchronous Javascript Execution happen
Developer Mozilla - Synchronous and asynchronous requests
How does javascript handle AJAX request in background.
Looking through your fiddle, you were cleaning up the dataURL before it was loaded, and therefore the thumbnails did not load at all. (The 404)
I made an updated fiddle https://jsfiddle.net/tLqch5x2/3/
function handleFiles(e){
var img = new Image();
img.onload = function(){
var width = 30;
var height = img.height * 30/img.width;
e.target.ic0.width = width ;
e.target.ic0.height = height ;
e.target.tc0.width = width/2 ;
e.target.tc0.height = height/2;
var ctx = e.target.ic0.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
ctx = e.target.tc0.getContext("2d");
ctx.drawImage(img, 0, 0, width/2, height/2);
URL.revokeObjectURL(img.src); //clean up memory here instead
};
img.src = URL.createObjectURL(e.target.files[0]);
//clean up to prevent memory leak
//URL.revokeObjectURL(img.src); //too soon
}
It still has a 404, but now from trying to post to a url that does not exist on jsfiddle.net. Please give it a try in your environment.
Note that there are also some other minor changes in both the html and the code. (improper naming the images, and no need to reference the existing form when creating the new form for submission)

Sending an image and JSON data to server using Ajax POST request?

I want to send an image the user selected from their machine along with form data all wrapped up in a JSON object and sent to server. I am using Node for the server. Is it possible to place the image in the JSON object along with the other form elements and read in Node?
The common ways I encountered are using the Base64 string approach: you encode your image into a Base64 string and set it as part of the JSON Object that you send over to your server.
Another approach seems to be using the Binary Data in JSON but I'd never tried this before so not much info from me.
Here's a code sample to do a Base64 encoding in Javascript. Specifically look for the method below
function getBase64Image(imgElem) {
// imgElem must be on the same server otherwise a cross-origin error will be thrown "SECURITY_ERR: DOM Exception 18"
var canvas = document.createElement("canvas");
canvas.width = imgElem.clientWidth;
canvas.height = imgElem.clientHeight;
var ctx = canvas.getContext("2d");
ctx.drawImage(imgElem, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
There is a way to achieve this: use image data.
In Javascript, on client side, the FileReader will allow you to read binary image data, and get them into a base64 encoded string.
On client side:
var file = $('.file-upload > input')[0].files[0];
function upload(file) {
var reader = new FileReader();
// when image data was read
reader.onload = function(event) {
// I usually remove the prefix to only keep data, but it depends on your server
var data = event.target.result.replace("data:"+ file.type +";base64,", '');
// make here your ajax call
$.ajax({
url: 'and_so_on',
json: {
data: data
}
});
// read data from file
reader.readAsDataURL(file);
}
On server side, you will received base64 encoded image that you can easilly translate into binary with the Buffer constructor
var buffer = new Buffer(data, 'base64');
Be warned that FileReader is not supported by all browsers

Automatically download and then upload a file in a chrome extension [duplicate]

This question already has an answer here:
Google chrome rehost image extension
(1 answer)
Closed 9 years ago.
I'm writing a Chrome Extension that automates downloading a file from one location and then uploading it to another. I've figured out how to do the download part using the HTML5 Filesystem API. However, I don't know how to then upload the file.
The issue is that the upload has to be done through a form using a "file" input element. I'd like to just tell the form where the file is located (I can get the location of the downloaded file from the Filesystem API). But I can't figure out a way of doing that programmatically.
Any ideas? Let me know if I can clarify anything!
EDIT: Another option is chrome.downloads extension API
EDIT: The File API looks promising (http://dev.w3.org/2006/webapi/FileAPI/). I also found this helpful: https://developer.mozilla.org/en-US/docs/Web/Guide/Using_FormData_Objects
So, this is the way I finally got it working. It's done in a Chrome extension. I don't think it's possible in a normal browser script, since the canvas.toDataURL function is used and will throw a security exception if the downloaded file is cross-origin. Anyway, here's a simplified version of how I did it. Luckily the files I'm downloading and uploading are images, so I can use the Image() class.
// load the image
var img = new Image();
img.src = "url to image";
// loaded
img.onload = function() {
// convert the image into a data url
var dataUrl = getImageDataUrl(this);
var blob = dataUrlToBlob(dataUrl);
// FormData will encapsulate the form, and understands blobs
var formData = new FormData();
// "image_name.png" can be whatever you want; it's what the
// host will see as the filename for this image (the host server
// that receives the posted form). it doesn't have to match the
// original filename, and can be arbitrary or not provided at all.
formData.append("file", blob, "image_name.png");
var request = new XMLHttpRequest();
// upload
request.open("POST", "url to form");
request.send(formData);
};
// converts an image into a dataurl
function getImageDataUrl(img) {
// Create an empty canvas element
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// NOTE: this will throw a cross-origin security exception
// if not done in an extension and if the image is cross-origin
return canvas.toDataURL("image/png");
}
// converts a dataurl into a blob
function dataUrlToBlob(dataUrl) {
var binary = atob(dataUrl.split(',')[1]);
var array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: 'image/png'});
}
I donĀ“t know if something like that exists, but you could dynamically create a form using Javascript, then updating the file input value and finally trigger the form's submit event.

Categories

Resources