8

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)
Community
  • 1
  • 1
Richard
  • 8,193
  • 28
  • 107
  • 228
  • Wait for the image to load using `img.onload` – Adam Azad Jan 21 '17 at 10:50
  • Thanks Adam, at what point must I do that? Before I create the Canvas? – Richard Jan 21 '17 at 10:51
  • what is the URL of the image where you got this error? – Vladu Ionut Jan 21 '17 at 11:37
  • It is: `https://lh4.googleusercontent.com/-0l4-kMbcaGo/AAAAAAAAAAI/AAAAAAAAO8M/lWHXvpefl6M/photo.jpg` or `https://www.google.de/images/srpr/logo11w.png`. ps. I have changed this to use jpg: `var dataURL = canvas.toDataURL("image/jpg");` – Richard Jan 21 '17 at 11:40
  • you can't get the image from the google server (the image loading has been blocked by CORS policy ) – Vladu Ionut Jan 21 '17 at 11:43
  • I see. I think I may need to change my approach to display the image then. I am getting the image after I do Firebase authentication and log in via a Google account. So what you say makes sense. – Richard Jan 21 '17 at 11:45
  • Thanks, I needed to use promises instead of callbacks and this question gave me some inspiration. – Islam Murtazaev Mar 18 '19 at 11:19

2 Answers2

11

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);
});
Adam Azad
  • 11,171
  • 5
  • 29
  • 70
2

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.");
      }

    });

}
Vladu Ionut
  • 8,075
  • 1
  • 19
  • 30