Richard
Richard

Reputation: 8935

Javascript Convert an URL to a BASE64 Image

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

Answers (3)

RBILLC
RBILLC

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

Vladu Ionut
Vladu Ionut

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

Adam Azad
Adam Azad

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

Related Questions