Converting HTML canvas to binary image data - javascript

I need help with HTML canvas element.I'm new to HTML.
I need to capture image from the webcam and send it to Microsoft cognitive API to find the emotions in the picture.
The API accepts image in octet-stream. The API is https://westus.dev.cognitive.microsoft.com/docs/services/5639d931ca73072154c1ce89/operations/563b31ea778daf121cc3a5fa
The code is
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext('2d');
dataUrl = canvas.toDataURL("image/png");
$.ajax({
url: apiUrl,
beforeSend: function(xhrObj) {
xhrObj.setRequestHeader("Content-Type", "application/octet-stream");
xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key", apiKey);
},
type: "POST",
data: dataUrl,
processData: false
}).done(function(response) {
...
});
HTTP Response code is 400 and the response message is as below.
{"error":{"code":"BadBody","message":"Invalid face image."}}
logging the dataUrl shows the data as
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAgAElEQVR4XqS92bOm2Vnlt7/pZGZVCU3IGkFCDhoxChDQBknQtGUBJtzd4Vvf+a7RUFklqVQC/xu.....
I have tried by removing data:image/png but no use.
I'm right canvas.toDataURL returns string form of base64 encoded data of the image. I can display this image on html page. I also did a right click saved it to local drive, and called API with file, it works.
So, the issue would be while moving the data from canvas element to the request body in binary format.
How can this be done ?. Thanks in advance for your help.

This might work. Try creating Blob from your dataUrl and pass it in to the ajax, as:
function createBlob(dataURL) {
var BASE64_MARKER = ';base64,';
if (dataURL.indexOf(BASE64_MARKER) == -1) {
var parts = dataURL.split(',');
var contentType = parts[0].split(':')[1];
var raw = decodeURIComponent(parts[1]);
return new Blob([raw], { type: contentType });
}
var parts = dataURL.split(BASE64_MARKER);
var contentType = parts[0].split(':')[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], { type: contentType });
}
in ajax, pass the blobData,
...
var blobData = createBlob(dataUrl);
$.ajax({
url : apiUrl,
beforeSend : function(xhrObj) {
xhrObj.setRequestHeader("Content-Type", "application/octet-stream");
xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key", apiKey);
},
type : "POST",
data : blobData,
processData : false
}).done(function(response) {...}

Related

Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded error

I know that the above question has been asked many times. But those did not solve my problem.
Here is the problem statement :
I am trying to download zip file using 'ajax', I have read many posts that FILE DOWNLOAD IS NOT POSSIBLE USING AJAX
So I tried to hack it, I don't know my approach is correct or not, I just tried it.
So what am I doing is .. I am sending the byte from My controller. to my ajax call, and trying to make it downloadable.
here is my ajax call
$.ajax({
type: "POST",
url: 'myURL',
data: JSON.stringify(ajaxData),
dataType: "json",
contentType: "application/json",
crossDomain: true,
accept: "application/zip",
success: function (data) {
// i get the data here from my controller
var sampleBytes = base64ToArrayBuffer(data);
saveByteArray([sampleBytes], 'black1x1.gif');
function base64ToArrayBuffer(base64) {
console.log("base64ToArrayBuffer called");
var binaryString = window.atob(base64);
var binaryLen = binaryString.length;
var bytes = new Uint8Array(binaryLen);
for (var i = 0; i < binaryLen; i++) {
var ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
return bytes;
}
var saveByteArray = (function () {
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
return function (data, name) {
var blob = new Blob(data, { type: "octet/stream" }),
url = window.URL.createObjectURL(blob);
a.href = url;
a.download = name;
a.click();
window.URL.revokeObjectURL(url);
};
}());
},
error: function (data) {
}
});
But when I convert the data to base64, I get the above error.
please, don't mind, I might be doing some foolish thing, I just tried,
can any one help me to solve the problem.

turning canvas.toDataURL DOMString url into a blob,

i am using cropperjs to crop images
Using the method getCroppedCanvas to get reference to the cropped canvas image
Using the canvas method canvas.toDataURL to compress the images into a jpeg files.
I don't use canvas.toBlob as its browser support is not clear.
My question is how can i turn the string i get from canvas.toDataURL into a blob so i can later use it in formData.
Update
server expect form data - multi-part
One method that I used while working on some tool is to convert the dataURL to Blob using this function:
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
}
And then you can send the Blob to the server using native xhr like this:
var blob = dataURLtoBlob(dataURI);
var fd = new FormData();
var xhr = new XMLHttpRequest ();
fd.append ('file', blob);
xhr.open('POST', '/endpoint', true);
xhr.onerror = function () {
console.log('error');
}
xhr.onload = function () {
console.log('success')
}
xhr.send (fd);
The blob builder code is taken from here: https://stackoverflow.com/a/30407840/2386736
use canvas.toDataURL to generate an JPEG image:
var JPEG_QUALITY=0.5;
var dataUrl = canvas.toDataURL('image/jpeg', JPEG_QUALITY).replace('data:image/jpeg;base64,', '');
And then send it to AJAX as data-multi-part:
jQuery.ajax({
url: 'php/upload.php',
data: dataUrl,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
}
});
Depending on what your server side expects to receive.
By the way: You wrote "I don't use canvas.toBlob as its browser support is not clear" - You can see the browser support here:

Send multipart request from ajax to spring controller without form?

I am uploading an image from the desktop, and converting this image into base code in javascript. After that I want to send this image base code to spring controller with the multipart request. But I am not using Form.
HTML
<input id="inputFileToLoad" type="file" onchange="encodeImageFileAsURL()">
JAVA SCRIPT
window.photoCakeUrl = '<c:url value="/media/image/upload"/>';
function encodeImageFileAsURL() {
var filesSelected = document.getElementById("inputFileToLoad").files;
if (filesSelected.length > 0) {
var fileToLoad = filesSelected[0];
var fileReader = new FileReader();
fileReader.onload = function (fileLoadedEvent) {
var srcData = fileLoadedEvent.target.result; // <--- data: base64
var newImage = document.createElement('img');
var photoCake = srcData;
newImage.src = srcData;
document.getElementById("imgTest").innerHTML = newImage.outerHTML;
var ajax1 = $.ajax({
type: 'POST',
url: photoCakeUrl,
processData: false, // important
contentType: false, // important
dataType: 'json',
data: {photoCak: photoCake}
});
});
},
fileReader.readAsDataURL(fileToLoad);
}
}
SPRING CONTROLLER:
#RequestMapping(value = "/media/image/upload", method = RequestMethod.POST)
#ResponseBody
public Map<String, String> productPictureUploadnew(MultipartHttpServletRequest request, HttpServletResponse response) {
Map<String, String> resp = new HashMap<>();
String photoCake = request.getParameter("photoCak");
System.out.println("photoCake " + photoCake);
return resp;
}
But when I am generating AJAX call then 500 error will comes. If i am using simply
public Map<String, String> productPictureUploadnew(HttpServletRequest
request, HttpServletResponse response)
Then it works. Mean when I am using MultipartHttpServletRequest place of HttpServletRequest
request then it is not works.
I got solution, We can use formData in javascript without using form in any JSP to send MultipartHttpServletRequest.
window.photoCakeUrl = '<c:url value="/media/image/upload"/>';
function encodeImageFileAsURL() {
var filesSelected = document.getElementById("inputFileToLoad").files;
if (filesSelected.length > 0) {
var fileToLoad = filesSelected[0];
var fileReader = new FileReader();
fileReader.onload = function (fileLoadedEvent) {
var srcData = fileLoadedEvent.target.result; // <--- data: base64
var newImage = document.createElement('img');
var photoCake = srcData;
newImage.src = srcData;
document.getElementById("imgTest").innerHTML = newImage.outerHTML;
var formData = new FormData();
formData.append("imgFile", document.getElementById("inputFileToLoad").files[0]);
var ajax1 = $.ajax({
type: 'POST',
url: photoCakeUrl,
dataType: 'json',
data: {photoCak: photoCake}
});
});
},
fileReader.readAsDataURL(fileToLoad);
}
}
var formData = new FormData();
formData.append("imgFile", document.getElementById("inputFileToLoad").files[0]);
Controller:
#RequestMapping(value = "/media/image/upload", method = RequestMethod.POST)
#ResponseBody
public Map<String, String> productPictureUploadnew(MultipartHttpServletRequest request, HttpServletResponse response) {
Map<String, String> resp = new HashMap<>();
System.out.println("fsasasafsafsafsafsa");
Iterator<String> itr = request.getFileNames();
String photoCake = request.getParameter("photoCak");
File file;
----------
-------
----------
return resp;
}
Thanks you, I hope this is help full for you guys.
You are sending it as multipart/form-data may be that's why HttpServletRequest isn't able to get your data , remove contentType option from ajax call then jquery will use the defaylt wiz. 'application/x-www-form-urlencoded; charset=UTF-8'
var ajax1 = $.ajax({
type: 'POST',
url: photoCakeUrl,
processData: false, // important
dataType: 'json',
data: {photoCak: photoCake}
});
This is how I would have done it:
window.photoCakeUrl = '<c:url value="/media/image/upload"/>';
window.URL = window.URL || window.webkitURL
function encodeImageFileAsURL() {
var filesSelected = $('#inputFileToLoad')[0].files;
if (filesSelected.length) {
var fileToLoad = filesSelected[0];
var img = new Image();
var formData = new FormData();
formData.append('imgFile', fileToLoad);
img.onload = function() {
// only append the image once it's loaded so we don't append broken images
$('#imgTest').html(this);
URL.revokeObjectURL(this.src); // Release memory
// Uploading a image when we can ensure it's a image that can be loaded
fetch(photoCakeUrl, {method: 'POST', body: formData});
}
img.onerror = function() {
// You didn't upload a image
}
img.src = URL.createObjectURL(srcData);
}
}
URL.createObjectURL is faster and uses less memory then a long base64 string that is also ~3x larger in size and uses 2x more memory since it's stored in utf16 and not utf8
You can use new Image which is a nicer sorter version of createElement('img')
Then I would also use Fetch instead of $.ajax cuz jQuery handle formData stupidly (need to set processData & contentType to false)
Then i would also add the accept="images/*" attribute to the file input to filter out images

Send image data to an API nodejs

I am attempting to send image data to a server API, though, the documentation included with the API only contains an example for JQuery, which I don't want to use with my nodejs project. Also, the JQuery example contains things that don't exist in Node, such as Blobs.
The example provided by the API is:
var sourceImage = new Image();
sourceImage.src = image; // add image data to object
sourceImage.onload = function() {
// Create a canvas with the desired dimensions
var canvas = document.createElement("canvas");
var dim = 256; // the image size
canvas.width = dim;
canvas.height = dim;
// Scale and draw the source image to the canvas
canvas.getContext("2d").drawImage(sourceImage, 0, 0, dim, dim);
var formDataWithCanvasImage = createFormData(canvas);
// Make ajax call here
}
function createFormData(canvas) {
var b64Img = canvas.toDataURL();
var binImg = dataURItoBlob(b64Img);
var fileName = new Date().getTime();// Name the file with the current timestamp and no extension
var fd = new FormData();
fd.append("imageClass", "preview");
fd.append("X-Requested-With", "Iframe");
fd.append("X-HTTP-Accept", "application/json, text/javascript, */*; q=0.01");
fd.append("file", binImg,fileName);
return fd;
}
$.ajax({
url: imageURL,
data: formDataWithCanvasImage,
processData: false,
contentType: false,
type: 'POST',
error: function() {alert("error uploading image");},
success: function(data){
console.log(data);
}
});
However, this wouldn't work with nodejs, because there is no canvas in nodejs, and no such thing as a blob. So, what I've done is read an image from the local disc and convert that buffer into an ArrayBuffer then encode it in analog-nico's request-promise like so:
let opts = {
uri: 'https://*****.com/api/images',
contentType: false,
processData: false,
type: 'POST',
formData: {
"imageClass": "application",
"X-Requested-With": "Iframe",
"X-HTTP-Accept": "application/json, text/javascript, */*; q=0.01"
"file": arraybuffer
},
}
request.post(opts).then(data => {....});
but the code throws TypeError: source.on is not a function. Has anyone encountered this problem before? How did you resolve it?
You could try loading the image from disk as a ReadStream and piping that using the request module to the API.
Rough Example:
fs.createReadStream(filename).pipe(request.put('http://example.com/api/images'))
The module is available via npm install request.

JavaScript code to display PDF file in browser from byte array (not base64 encoded)

I am working with an ASP .NET Web API where the POST function of the API is returning a byte array like so in the C# code:
return Ok(outputStream.ToArray());
Where outputStream is of type System.IO.MemoryStream and the ToArray() is returning a byte array.
This function is returning a PDF stream that I want to display to the users in their browser or at least let them save it.
I've ran across a JavaScript example from Stack Overflow that shows how to get a Blob from a Base64 encoded string and display it and I was able to get it working when the API returned a Base64 encoded string; however, I wasn't able to get it working when the API was return the byte array as shown before.
May I get some code examples of how to get it working when the API is returning a byte array?
My JQuery Ajax POST request looks like:
function LoadPDF() {
var args = [];
var x = $.ajax({
type: "POST",
url: "DirectoryQuery.aspx/GetFullPdf",
contentType: "application/json; charset=UTF-8",
responseType: "text/plain; charset=UTF-8",
async: true,
dataType: "json",
processData: "false",
success: OnSuccess,
error: OnErrorCall
});
function OnSuccess(response) {
var byteArray = new Uint8Array(response.d);
saveTextAsFile("document.pdf", byteArray);
}
function OnErrorCall(response) {
console.log(response);
//location.reload();
}
}
Try file-save-as.js, like this:
function saveTextAsFile(fileNameToSaveAs, textToWrite) {
/* Saves a text string as a blob file*/
var ie = navigator.userAgent.match(/MSIE\s([\d.]+)/),
ie11 = navigator.userAgent.match(/Trident\/7.0/) && navigator.userAgent.match(/rv:11/),
ieEDGE = navigator.userAgent.match(/Edge/g),
ieVer = (ie ? ie[1] : (ie11 ? 11 : (ieEDGE ? 12 : -1)));
if (ie && ieVer < 10) {
console.log("No blobs on IE ver<10");
return;
}
var textFileAsBlob = new Blob([textToWrite], {
type: 'text/plain'
});
if (ieVer > -1) {
window.navigator.msSaveBlob(textFileAsBlob, fileNameToSaveAs);
} else {
var downloadLink = document.createElement("a");
downloadLink.download = fileNameToSaveAs;
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
downloadLink.onclick = function (e) { document.body.removeChild(e.target); };
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
downloadLink.click();
}
}

Categories