Skip to content Skip to sidebar Skip to footer

Render An Image Binary Data Into A Img Or Canvas Tag

I have a route which I use to request images, something like this: /api/image it returns the body the file data, something like: ����JFIF��C��C��� ��

Solution 1:

Well, the top data you've shown is binary and as such, just prepending the dataURL stuff wont work. You'll need to either do something different with the binary data or send back actual base64 encoded data instead.

Here's a compound example. It shows:

  • reading of a file on disk
  • displaying it
  • uploading it
  • creating an altered copy with GD unless it's an svg
  • sending it back to the browser as binary data
  • catching it
  • transforming it into a blob and then a dataURL
  • before finally setting the src attribute of an image with it.
  • base64 encoding it, adding the dataURL stuff and outputtting it.
  • retrieving a off-site or cross-protocol image and drawing on a canvas without tainting it.

base64.php

<?php
    // usefull for images without CORS header
    if (isset($_GET['filename']) == true)
    {
        $filename = urldecode( $_GET['filename'] );
        $data = file_get_contents($filename);
        $finfo = new finfo(FILEINFO_MIME);
        $mimeType = $finfo->buffer($data);
        Header("Content-Type: $mimeType");                  // use the currently detected mime-type
        echo $data;
        die;
    }

    if (isset($_FILES['upload']) == true)
    {
        $alterResult = true;

        // GD wont load svgs :(
        if (strcmp(trim($_FILES['upload']['type']),"image/svg+xml") == 0)
            $alterResult = false;

        // expecting a form element with the type of 'file' and the name of 'upload' - accepting 1 file max
        $data = file_get_contents( $_FILES['upload']['tmp_name'] );

        // draw copy of image, invert the colours, guassian blur 5 times, draw inverted,bluury image beside unaltered copy
        if ($alterResult == true)
        {
            $mImage = imagecreatefromstring ($data);
            $output = imagecreatetruecolor(imagesx($mImage) * 2, imagesy($mImage));
            imagesavealpha ( $mImage , true);
            imagesavealpha ( $output , true);
            imagecopy($output, $mImage, 0, 0, 0, 0, imagesx($mImage) - 1, imagesy($mImage) - 1);
            imagefilter($mImage,IMG_FILTER_NEGATE);
            imagefilter($mImage,IMG_FILTER_GAUSSIAN_BLUR);
            imagefilter($mImage,IMG_FILTER_GAUSSIAN_BLUR);
            imagefilter($mImage,IMG_FILTER_GAUSSIAN_BLUR);
            imagefilter($mImage,IMG_FILTER_GAUSSIAN_BLUR);
            imagefilter($mImage,IMG_FILTER_GAUSSIAN_BLUR);
            imagecopy($output, $mImage, imagesx($mImage), 0, 0, 0, imagesx($mImage) - 1, imagesy($mImage) - 1);
            Header("Content-Type: image/png");
            imagepng($output);
            imagedestroy($mImage);
            imagedestroy($output);
            die;
        }
        // it's an svg, so just return byte-for-byte what we received
        else
        {
            $finfo = new finfo(FILEINFO_MIME);
            $mimeType = $finfo->buffer($data);
            $mimeType = preg_replace("/text\/plain/", "image/svg+xml", $mimeType);  // svgs are wrongly reported as text files
            Header("Content-Type: $mimeType");                  // use the currently detected mime-type
            echo $data;
            die;
        }
    }
?><!doctype html>
<html>
<head>
<script>
function byId(id){return document.getElementById(id)}
function newEl(tag){return document.createElement(tag)}
window.addEventListener('load',onDocLoaded);
function onDocLoaded(evt)
{
    byId('fileInput').addEventListener('change', onFileChosen);

    // cross origin test
    var can = newEl('canvas');
    var ctx = can.getContext('2d');
    var srcImg = byId('crossOrigin');
    can.width = srcImg.naturalWidth;
    can.height = srcImg.naturalHeight;
    ctx.drawImage(srcImg, 0,0);
    document.body.appendChild(can);
    console.log( can.toDataURL() );
}
function ajaxGet(url, onLoad, onError)
{
    var ajax = new XMLHttpRequest();
    ajax.onload = function(evt){onLoad(this);}
    ajax.onerror = function(evt){onError(this);}
    ajax.open("GET", url, true);
    ajax.send();
}
function ajaxGetBinary(url, onLoad, onError)
{
    var ajax = new XMLHttpRequest();
    ajax.responseType = 'arraybuffer';
    ajax.onload = function(evt){onLoad(this);}
    ajax.onerror = function(evt){onError(this);}
    ajax.open("GET", url, true);
    ajax.send();
}
function ajaxPostForm(url, formElem, onLoad, onError)
{
    var ajax = new XMLHttpRequest();
    ajax.open("POST", url, true);
    ajax.responseType = 'arraybuffer';
    ajax.onload = function(evt){onLoad(this);}
    ajax.onerror = function(evt){onError(this);}
    ajax.send( new FormData(formElem) );
}

function onFileChosen(evt)
{
    // just clear the images if no file selected
    if (this.files.length < 1)
    {
        byId('beforeImg').src = '';
        byId('afterImg').src = '';
        return;
    }

    var file = this.files[0];       // used to set the mime-type of the file when we get it back
    /*
        ==========Before upload/download==========
    */
    let fileReader = new FileReader();
    fileReader.onload = function(evt){byId('beforeImg').src=this.result;}
    fileReader.readAsDataURL(file); 

    /*
        ==========After upload/download==========
        send the file to the backend (also this source-file), then the back-end will read the temporary file and output it.
        we catch this binary output and make an image element with it
    */
    ajaxPostForm('<?php echo $_SERVER['PHP_SELF']; ?>', byId('mForm'), onPostOkay, function(){});
    function onPostOkay(ajax)
    {
        let arrayBuffer = ajax.response;
        if (arrayBuffer)
        {
            let byteArray = new Uint8Array(arrayBuffer);
            let blob = new Blob([byteArray], {type: file.type });
            byId('afterImg').src = URL.createObjectURL(blob);
        }
    }
}
</script>
<style>
.panel
{
    display: inline-block;
    padding: 8px;
    border-radius: 8px;
    border: solid 1px black;
    text-align: center;
}
</style>
</head>
<body>
    <!-- RESULT of below: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA......." -->
    <img id='crossOrigin' src='base64.php?filename=https%3A%2F%2Fmy.totalwellbeingdiet.com%2Fimg%2Fcsiro%2Flogos%2Fcsiro.png'/>

    <!-- RESULT of below: "Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported." -->
    <!-- <img id='crossOrigin' src='https://my.totalwellbeingdiet.com/img/csiro/logos/csiro.png'/> -->

    <form id='mForm' method='post' enctype="multipart/form-data">
        <input id='fileInput' type='file' name='upload'/>
    </form>
    <div class='panel'>
        <img id='beforeImg'/><br>
        <strong>Before upload</strong>
    </div>
    <div class='panel'>
        <img id='afterImg'/><br>
        <strong>After upload/mod/download</strong>
    </div>
</body>
</html>

Solution 2:

You should not construct a blob yourself, the responseType should be blob

And don't use jquery's Ajax method for binary...


Post a Comment for "Render An Image Binary Data Into A Img Or Canvas Tag"