1

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��� ��    
�����+�}Yϭ�F39M>���������>���;��ˋ��uXʽ�w�ڤx\-[2g��k�S���H���m
[�V?[_W����#��v��}6�[��F�F�%����n�...

what I'm trying to do is something like this:

<canvas>

then i did this:

code:

var canvas = $('canvas');
var blob = new Blob([file], {type: 'image/png'});
var url = URL.createObjectURL(blob);
var img = new Image;
var ctx = canvas.getContext('2d');

$(img).on('load',function(event) {
   ctx.drawImage(img, 0, 0);
   URL.revokeObjectURL(url);
}).each(function(){
     if(this.complete) { $(this).load() }
});
img.src = url;

I also appended the file directly to the image source attribute like:

img.src = 'data:image/png;base64,' + file

but it didn't work

What I get is that the resource is not found and it throws a 404 error:

> GET blob:http://localhost:3000/2a86de11-b565-4578-8ec1-2c20cbdae739 404 (Not Found)

I'm a bit confused, what am I doing wrong?

edit: i just did this:

var blob = new Blob([file], {type: 'image/png'});
var reader = new FileReader();

reader.onload = function (e) {
  suggestion.img.attr('src', e.target.result);
};
reader.readAsDataURL(blob);

but it doesnt show the image either

Abdul Hamid
  • 168
  • 1
  • 7
  • 25
  • What is different in this question from [this one](http://stackoverflow.com/questions/39711888/turn-image-binary-data-into-img-tag) ? – Kaiido Sep 27 '16 at 01:27

2 Answers2

2

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>
enhzflep
  • 12,927
  • 2
  • 32
  • 51
  • Thank you for your answer! The problem is that i cant change the backend, i just get that binary and i have to deal with it – Abdul Hamid Sep 26 '16 at 20:36
  • No worries mate, you're welcome. Hey - hang on a minute. Is there any reason you can't simply do `img.src = "/api/image";` - I mean it returns image data - that's what the browser wants when it tries to read the resource specified as the source, who cares what the url is? Is it possible this is being needlessly complicated? – enhzflep Sep 27 '16 at 05:42
  • Amazing! very instructive! – Abdul Hamid Sep 27 '16 at 15:08
0

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

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

Endless
  • 34,080
  • 13
  • 108
  • 131
  • How do you know OP is using `$.ajax` ? Why didn't you voted to close as a dupe of the other one you just answered ? What is this idea that you should not create a Blob yourself (even if in OP's case, it's indeed an X/Y problem) ? If you were right, why is there a Blob Constructor ? How would your reconstruct files from chunks? – Kaiido Sep 27 '16 at 01:30
  • he was right, i actually solved the problem by not using jquerys ajax, now it works perfectly and all i had to do was to change to XMLHtmlRequest – Abdul Hamid Sep 27 '16 at 15:08