-1

Trying to store user image to server. Very new to this, so I just started looking around and I found the following code:

function getBase64Image( imgElem ) {

    let canvas = document.createElement( 'canvas' );
    canvas.width = imgElem.clientWidth;
    canvas.height = imgElem.clientHeight;

    let ctx = canvas.getContext( '2d' );
    ctx.drawImage(imgElem, 0, 0);

    let dataURL = canvas.toDataURL( 'image/png' );
    return dataURL.replace( /^data:image\/(png|jpg);base64,/, '' );

}

I'm using this function to store a user inputed image on my local server, but I get the following error:

factories.js:1 Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(HTMLImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap)'getBase64Image @ factories.js:1SubmitRecipe @ recipe-submission.js:1onclick @ submitrecipe:43

I am grabbing the image with an input tag that has type set to file. Here is my code:

HTML

<label>Image</label>
<span class='err-message' id='image-err'></span></br>
<input type="file" id='image'>

JS

function SubmitRecipe() {

    //sets the recipe object and its keys
    let recipe = { alias: null, description: null, category: null, instructions: null, image: null };

    let img = document.getElementById( 'image' );
    recipe.image = JSON.stringify( getBase64Image( img ) );

}

I believe the problem has to do with the fact that the element image is an HTMLInputElement, and that isn't what drawImage expects. I just have no idea how to solve it.

All help is appreciated.

user5854440
  • 271
  • 4
  • 14
  • Did you place canvas element in your html?? – kernal_lora Sep 26 '16 at 00:46
  • If you have Are you sure this function called on succesfull loading of image?? I mean onload event – kernal_lora Sep 26 '16 at 00:49
  • @kernallora Isn't that what `let canvas = document.createElement( 'canvas' );` is doing? And the error is coming from `ctx.drawImage(imgElem, 0, 0);`, so I believe the function is being called. – user5854440 Sep 26 '16 at 00:55
  • The problem is that your imgElem isn't actually an image element, but is instead an input element - one that can accept images. So while the data ultimately held by the element is compatible with `.drawImage`, the element itself is not. – enhzflep Sep 26 '16 at 02:11

2 Answers2

1

You can make use of the FileReader object to do this. The really neat thing is it can even give us the data as a data URL in the first place - this means we dont convert/recompress images in the process. A jpg wont be enlarged by saving as a png, nor will a png lose detail by conversion to a jpg. Since we're no longer using the canvas to create the URL, we no longer have control over the precise mime-type. As such, we may have a jpg,png or jpeg file - this just means adding |jpeg to the regex so we replace instances of it too.

You can also listen to the change event of the file input. Doing so will allow you to check the size and type of file to ensure they are acceptable. (Someone could select a 100MB text file using the file input)

function byId(id){return document.getElementById(id)}
function newEl(tagName){return document.createElement(tagName)}

window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
 byId('goBtn').addEventListener('click', onBtnClicked, false);
}

function onBtnClicked(evt)
{
 let imgInput = byId('imageInput');
 let chosenFile = imgInput.files[0];
 let fr = new FileReader();
 fr.onload = function(evt)
    {
                    var img = newEl('img');
                    document.body.appendChild(img);
                    img.src = this.result;
      
     var dataURL = this.result.replace( /^data:image\/(png|jpg|jpeg);base64,/, '' );
     let recipe = { alias: null, description: null, category: null, instructions: null, image: JSON.stringify(dataURL) };
     console.log(recipe);
    };
 fr.readAsDataURL(chosenFile);
}
<label>Image</label>
 <span class='err-message' id='image-err'></span></br>
 <input type="file" id='imageInput'>
 <button id='goBtn'>GO</button>
enhzflep
  • 12,927
  • 2
  • 32
  • 51
  • This works. Out of curiosity, how would I display this image back onto the DOM? I.e. put it as the source of an image tag? – user5854440 Sep 26 '16 at 13:48
  • Funnily enough, that's actually exactly what you do! I've updated my demo to show a preview of the image. Notice that I'm doing it to the raw result of the filereader, _without_ changing the string with the regex or json-ing. The whole idea of base64 of the source is that it's transmittable (submittable) text already, as-is. So, yuo may just set the image attribute of your recipe to the base64 result and then `json.stringify` the whole thing instead of changing the image source and `json.stringify`ing it before storing to the image attribute of the recipe object. – enhzflep Sep 26 '16 at 15:12
  • I.e - `fr.onload = function(evt){let recipe = { alias: null, description: null, category: null, instructions: null, image: this.result }; console.log(recipe);}` – enhzflep Sep 26 '16 at 15:15
  • Pefrect! So if I were to store this on the back end, I would use `JSON.stringify()`, and then when I returned it to the front end, I would just use `JSON.parse()` and then display it in an image tag? – user5854440 Sep 26 '16 at 21:30
  • Yup, that's what I'd consider doing. Here's another answer you may find helpfu - note inside the `onPostOkay` function that an image is created and has its source set with a base64 string. http://stackoverflow.com/questions/39709188/render-an-image-binary-data-into-a-img-or-canvas-tag/39711448#39711448 – enhzflep Sep 27 '16 at 11:16
0

The question is you couldn't get file path pass through input in the chrome、fireFox,IE8 can get it when choosed file.