1

I am converting an image to Base64 from Image URL using canvas. The approach I am taking is similar to below 2 links but the converted Base64 string is not opening up the image when viewed in Chrome Tab.

Links

CONVERT Image url to Base64 (1st answer with 81 thumbs up).

https://jsfiddle.net/3qshvc54/

I tried consoling the img, canvas, ctx in fiddle and my code. The console output is the same. Please see below screenshots. If I do it by fiddle the converted Base64 URL opens up fine in a new tab, but the one generated from my code does not display an image when opened up in a new tab.

I am using same Image URL in my code and Fiddle

Console screenshot when running from my code

enter image description here

Console screenshot when running from Fiddle

enter image description here

The Base64 generated from my code:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQAAABDCAYAAAAlFqMKAAAApklEQVR4Xu3VsQ0AIAwEMbL/0tShQLrejHB6kzneKjB67AKCPIsQRJD/J2EhFmIh6ZAigwwyyKQCyCCTBuPKIIMMMqkAMsikwbgyyCCDTCqADDJpMK4MMsggkwogg0wajCuDDDLIpALIIJMG48oggwwyqQAyyKTBuDLIIINMKoAMMmkwrgwyyCCTCiCDTBqMK4MMMsikAsggkwbjyiCDDDKpADL/XBfFQABED54eAwAAAABJRU5ErkJggg==

I tried validating the same online it shows that the Base64 string is valid:

enter image description here

When I try to decode the same:

It seems that the result of decoding is a binary data (MIME type detected as “image/png”) and because of this the data from “Text” may be damaged during the output.

enter image description here

My code:

<div className='image-root'>
    <img id={`imageBlock-${props.photoBoxID}`} className="multi-image-photo" src={props.imgUrl} alt="photo"></img>
</div>

getBase64Image = (img) => {
    img.crossOrigin = 'Anonymous';
    var canvas = document.createElement("CANVAS");
    canvas.width = img.width;
    canvas.height = img.height;
    console.log('img', img);
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    console.log('canvas', canvas);
    console.log('ctx', ctx);
    var dataURL = canvas.toDataURL();
    console.log('dataURL', dataURL);
}

componentDidUpdate(prevProps, prevState){
    if(this.props.isUploaded && (prevProps.isUploaded !== this.props.isUploaded)){
        let imageRef = document.getElementById(`imageBlock-${this.props.photoBoxID}`);
        imageRef.onload = this.getBase64Image(imageRef);
    }    
}

Can you suggest why the Base64 string generated is not opening up fine if converted from my code, though it opens up from Fiddle? Is it corrupted?

Shantanu Tomar
  • 1,572
  • 7
  • 38
  • 64
  • When I copy the Data-uri to the address-bar, I get a 68x67px transparent image. and testing your fiddle, it's full of CORS-messages. – Thomas Sep 08 '19 at 07:57

2 Answers2

1

onload is an event you need function there and in that function you need to update src.

componentDidUpdate(prevProps, prevState){
    if(this.props.isUploaded && (prevProps.isUploaded !== this.props.isUploaded)){
        let imageRef = document.getElementById(`imageBlock-${this.props.photoBoxID}`);
        imageRef.onload = () => {
           delete imageRef.onload;
           imageRef.src = this.getBase64Image(imageRef);
        };
    }    
}

but better is to add base64 image to state and don't use getElementById directly in React applications.

EDIT:

for infinite loop I would create 2 images, if you need to render image on canvas you usually render outside of DOM:

componentDidUpdate(prevProps, prevState){
    if(this.props.isUploaded && (prevProps.isUploaded !== this.props.isUploaded)){
        let imageRef = document.getElementById(`imageBlock-${this.props.photoBoxID}`);
        var srcImg = new Image();
        srcImg.onload = () => {
           imageRef.src = this.getBase64Image(srcImg);
        };
        srcImg.src = prevProps.imgUrl;
    }    
}

and remove the props from JSX template you will not need it.

And side note: if you have big image it's better to create object URL from blob, because there is limit of data that can be put into URL (for smaller images it should not matter):

function asObjectUrl(canvas) {
    return new Promise(function(resolve) {
        canvas.toBlob(function(blob) {
            resolve(URL.createObjectURL(blob));
        });
    });
}

The object url need to be removed when not needed to not create memory leaks (use URL.revokeObjectURL)

jcubic
  • 61,973
  • 54
  • 229
  • 402
  • 1
    And here you go for a crazy fun infinite loop eating all the memory! – Kaiido Sep 08 '19 at 10:09
  • @Kaiido fixed the problem. – jcubic Sep 08 '19 at 10:11
  • @jcubic Thanks. Now I am getting the thumbnail which is opening fine in a new tab, but it is still an infinite loop over onload. I can't use refs as I am working on react15 so have to use getElementByID approach. – Shantanu Tomar Sep 09 '19 at 05:49
  • 1
    @ShantanuTomar you can use two images one outside of DOM to get the source image and one as destination. – jcubic Sep 09 '19 at 08:17
  • @jcubic Thanks. I will check that. On your second point, can you please let me know If my image size is ranging from 10KB to 10MB do I need to use blob? What is your parameter for 'big image' – Shantanu Tomar Sep 09 '19 at 08:42
  • @ShantanuTomar its depend of length of base64 and depend on browser. Se SO question: [Data protocol URL size limitations](https://stackoverflow.com/a/695167/387194) – jcubic Sep 09 '19 at 08:46
  • @jcubic Can you please check the EDIT 1 in question. I have edited my question. – Shantanu Tomar Sep 09 '19 at 12:59
  • 1
    @ShantanuTomar please don't edit your question with different question in SO there should be one specific question and one specific answer to the question. You said it working but now you have different question, please ask another one with different description, so it will be useful if someone search for similar thing. – jcubic Sep 09 '19 at 14:53
  • @jcubic Corrected – Shantanu Tomar Sep 10 '19 at 04:33
0

change your img's src attribute adding src="data:image/jpeg;base64 tag with the encoded string like below:

 <img id={`imageBlock-${props.photoBoxID}`} className="multi-image-photo" src="data:image/jpeg;base64, ${props.imgUrl}" alt="photo"></img>

This should work.
N.B.: Please check your imageString variable carefully, I might have used the wrong one, not sure.

user404
  • 1,934
  • 1
  • 16
  • 32