Javascript Convert an URL to a BASE64 Image
Asked Answered
P

3

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)
Plowshare answered 21/1, 2017 at 10:47 Comment(7)
Wait for the image to load using img.onloadNeedlewoman
Thanks Adam, at what point must I do that? Before I create the Canvas?Plowshare
what is the URL of the image where you got this error?Hebetic
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");Plowshare
you can't get the image from the google server (the image loading has been blocked by CORS policy )Hebetic
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.Plowshare
Thanks, I needed to use promises instead of callbacks and this question gave me some inspiration.Gotthelf
N
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);
});
Needlewoman answered 21/1, 2017 at 10:59 Comment(3)
Thanks, I will give it a go.Plowshare
Worked like a charm!Courtney
This will break if the image is a GIFMurr
H
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.");
      }

    });

}
Hebetic answered 21/1, 2017 at 11:18 Comment(1)
Thank you. I have tried but am now getting a security error (see UPDATE above).Plowshare
D
0

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);           
                
Dryclean answered 31/1, 2024 at 22:51 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.