Reputation: 8935
I am building an Ionic2 app. I am trying to convert an image url to a base64 image. I have found this which I am trying to make use of.
I have the following code:
var imgUrl = 'https://www.google.de/images/srpr/logo11w.png';
let base64image = this.getBase64Image(imgUrl);
console.log(base64image);
and
public getBase64Image(imgUrl) {
var img = new Image();
img.src = imgUrl;
img.setAttribute('crossOrigin', 'anonymous');
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
But, it outputs the following:
data:,
I get no errors, but expect a base64 image.
My code must me incorrect. Can anyone please advise how to convert the url to a base64 image?
Thanks
UPDATE
Thank you to the feedback from the guys below, I have followed their advise to wit for the image to load. Now I have the following code:
public getBase64Image(imgUrl): Promise<string> {
return new Promise<string>(resolve => {
var img = new Image();
img.src = imgUrl;
img.setAttribute('crossOrigin', 'anonymous');
img.onload = (() => {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL("image/png");
//console.log('UgetBase64Image.dataURL ', dataURL);
resolve(dataURL.replace(/^data:image\/(png|jpg);base64,/, ""));
});
});
}
usage:
let promise64: Promise<string> = this.getBase64Image(personModel.avatar);
promise64.then((data) => {
personModel.avatar64 = data;
});
This does seem to create a base64 image when I run the console.log.
However, I do get the following error:
Error: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported. at HTMLImageElement.img.onload (utilityService.ts:80)
Line 80: var dataURL = canvas.toDataURL("image/png");
I would have thought the following code would resolve this security issue, but to no avail:
img.setAttribute('crossOrigin', 'anonymous');
More info:
Full error:
:8100/iVBORw0KGgoAAAANSUhEUgAAAbgAAAG5CAYAAAD8liEWAAAgAElEQVR4Xty9B3NkR5Ksm…bNkFj80enI0JnJ80+gTsx2sbrX9zhp7k1oOOPZ5K7Oh/AvN0hP6tzZ6QAAAAAElFTkSuQmCC:1 GET http://localhost:8100/iVBORw0KGgoAAAANSUhEUgAAAbgAAAG5CAYAAAD8liEWAAAgAElEQ…t3bNkFj80enI0JnJ80+gTsx2sbrX9zhp7k1oOOPZ5K7Oh/AvN0hP6tzZ6QAAAAAElFTkSuQmCC net::ERR_EMPTY_RESPONSE
polyfills.js:3 POST http://localhost:8080/jbosswildfly-1.0/person/updatetime 400 (Bad Request)
e @ polyfills.js:3
t.scheduleTask @ polyfills.js:3
e.scheduleMacroTask @ polyfills.js:3
(anonymous) @ polyfills.js:3
send @ VM9549:3
(anonymous) @ xhr_backend.js:117
Observable.subscribe @ Observable.js:45
MapOperator.call @ map.js:54
Observable.subscribe @ Observable.js:42
(anonymous) @ personService.ts:141
t @ polyfills.js:3
PersonService.updateTimeStamps @ personService.ts:140
(anonymous) @ searchjobsParent.ts:109
t.invoke @ polyfills.js:3
onInvoke @ ng_zone.js:236
t.invoke @ polyfills.js:3
onInvoke @ ng_zone.js:236
t.invoke @ polyfills.js:3
e.run @ polyfills.js:3
(anonymous) @ polyfills.js:3
t.invokeTask @ polyfills.js:3
onInvokeTask @ ng_zone.js:227
t.invokeTask @ polyfills.js:3
onInvokeTask @ ng_zone.js:227
t.invokeTask @ polyfills.js:3
e.runTask @ polyfills.js:3
i @ polyfills.js:3
polyfills.js:3 GET http://localhost:8080/jbosswildfly-1.0/person/list/favouritejob/null/0/ 400 (Bad Request)
EXCEPTION: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
ErrorHandler.handleError @ error_handler.js:47
IonicErrorHandler.handleError @ ionic-error-handler.js:56
next @ application_ref.js:272
schedulerFn @ async.js:82
SafeSubscriber.__tryOrUnsub @ Subscriber.js:223
SafeSubscriber.next @ Subscriber.js:172
Subscriber._next @ Subscriber.js:125
Subscriber.next @ Subscriber.js:89
Subject.next @ Subject.js:55
EventEmitter.emit @ async.js:74
NgZone.triggerError @ ng_zone.js:278
onHandleError @ ng_zone.js:257
t.handleError @ polyfills.js:3
e.runTask @ polyfills.js:3
invoke @ polyfills.js:3
error_handler.js:52 ORIGINAL STACKTRACE:
ErrorHandler.handleError @ error_handler.js:52
IonicErrorHandler.handleError @ ionic-error-handler.js:56
next @ application_ref.js:272
schedulerFn @ async.js:82
SafeSubscriber.__tryOrUnsub @ Subscriber.js:223
SafeSubscriber.next @ Subscriber.js:172
Subscriber._next @ Subscriber.js:125
Subscriber.next @ Subscriber.js:89
Subject.next @ Subject.js:55
EventEmitter.emit @ async.js:74
NgZone.triggerError @ ng_zone.js:278
onHandleError @ ng_zone.js:257
t.handleError @ polyfills.js:3
e.runTask @ polyfills.js:3
invoke @ polyfills.js:3
error_handler.js:53 Error: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
at HTMLImageElement.img.onload (utilityService.ts:82)
at HTMLImageElement.n [as _onload] (polyfills.js:2)
at t.invokeTask (polyfills.js:3)
at Object.onInvokeTask (ng_zone.js:227)
at t.invokeTask (polyfills.js:3)
at e.runTask (polyfills.js:3)
at HTMLImageElement.invoke (polyfills.js:3)
Upvotes: 8
Views: 36379
Reputation: 190
I have created a function using async/await from several different sources as follows for ease of integration with other functions:
async function getImgBase64(url) {
try {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.src = url;
await img.decode();
var canvas = document.createElement('CANVAS');
canvas.height = img.height;
canvas.width = img.width;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
var dataURL = dataURL = await canvas.toDataURL();
}
catch (e) {
console.log({ url });
console.log({ e });
}
//Return
return new Promise(resolve => resolve(dataURL));
}
Called from within another async function:
let base64Icon = await getImgBase64(url);
Upvotes: 0
Reputation: 8183
To can use images from another origin(server) on a canvas, the image must be served with CORS headers. This page on MDN explains it.
var imgUrl = 'https://www.google.de/images/srpr/logo11w.png';
var imgUrl = 'https://dl.dropboxusercontent.com/u/139992952/coffee.png';
let base64image = getBase64Image(imgUrl).then(function(base64image) {
console.log(base64image);
}, function(reason) {
console.log(reason); // Error!
});
function getBase64Image(imgUrl) {
return new Promise(
function(resolve, reject) {
var img = new Image();
img.src = imgUrl;
img.setAttribute('crossOrigin', 'anonymous');
img.onload = function() {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL("image/png");
resolve(dataURL.replace(/^data:image\/(png|jpg);base64,/, ""));
}
img.onerror = function() {
reject("The image could not be loaded.");
}
});
}
Upvotes: 2
Reputation: 11297
Image instance fires onload
event when the image is fully loaded. With this, another issue comes in which is dealing asynchronous functions. To be able to use what getBase64Image
uses, a callback function must be used. Without a callback function, the function returns undefined
let base64image = this.getBase64Image(imgUrl);
console.log(base64image); // undefined
Adjusted function
public getBase64Image(imgUrl, callback) {
var img = new Image();
// onload fires when the image is fully loadded, and has width and height
img.onload = function(){
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL("image/png"),
dataURL = dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
callback(dataURL); // the base64 string
};
// set attributes and src
img.setAttribute('crossOrigin', 'anonymous'); //
img.src = imgUrl;
}
Usage:
this.getBase64Image(imgUrl, function(base64image){
console.log(base64image);
});
Upvotes: 11