0

Okay, I know that there are loads of subjects that look identical to this one on SO, but none of them have fixed my issue...

I'm trying to grab an image from a file input and throw it onto a canvas so that I can later turn it into a base-64 image... But I've hit a snag in the process that I was not expecting, in drawing the image to the canvas...

Taking the following HTML:

<input type="file" id="fileIn" onchange="preview()"><br>
<img id="filePreview"><br>
<canvas id="fileCanvas"></canvas>

And the following script:

var dataurl = '';
function preview(){
    document.getElementById('filePreview').src = URL.createObjectURL(document.getElementById('fileIn').files[0]);
    document.getElementById('filePreview').onload = showInCanvas;
    cnv = document.getElementById('fileCanvas');
    ctx = cnv.getContext('2d');
}
function showInCanvas(){
    cnv.style.width = document.getElementById('filePreview').naturalWidth + 'px';
    cnv.width = document.getElementById('filePreview').naturalWidth;
    cnv.style.height = document.getElementById('filePreview').naturalHeight + 'px';
    cnv.height = document.getElementById('filePreview').naturalHeight + 'px';
    ctx.clearRect(0, 0, cnv.width, cnv.height);
    ctx.drawImage(document.getElementById('filePreview'), 0, 0);
    dataurl = cnv.toDataURL('image/png');
}

The problem I'm having is that the image simply refuses to draw onto the canvas. Even going into the console and drawing the image to the canvas manually, after the script has run, it still refuses to draw. Because of this, the image data simply runs through as data:,

It's an https site, if that affects anything.

For clarification, here's my question:

Why is the canvas refusing to render this image? How can I fix this issue?

  • Data URI's are already Base64, so your `URL.createObjectURL` should be all that you need (assuming it works). – I wrestled a bear once. Dec 02 '16 at 16:16
  • and if it doesn't you need to show that code. your question needs to include all the code necessary to re-create the problem. – I wrestled a bear once. Dec 02 '16 at 16:17
  • Oops, I left some of my shorthand for document.getElementById in there. Should be out now. I just slapped it all (the code in the post) into an HTML editor and got the exact same result... – Aaron Adams Dec 02 '16 at 16:22
  • The createObjectURL is actually just a blob to a fake file... it is not base-64 encoded... the URL from the createObjectURL is `blob:https://website.com/55550831-2c43-44a6-8dff-ba71ede661b4` – Aaron Adams Dec 02 '16 at 16:23
  • well that's the problem. the only way you can load an image into a canvas is from a real url where the file actually lives, or from a base64, which you can get easily from a file upload.. – I wrestled a bear once. Dec 02 '16 at 16:32
  • 1
    To the person that tried to edit my post... The site is literally an HTTPS site. Changing that to HTTP in the post would make the post inaccurate. I know HTTPS has different security privelages than HTTP in browsers... – Aaron Adams Dec 02 '16 at 16:33
  • How should I go about grabbing the base-64 from the file then? I thought this was the preferred method, based on my research. – Aaron Adams Dec 02 '16 at 16:34
  • Do you intend to process the image or just convert it to data-URI? If the latter you don't need to go by canvas. Just use FileReader and read the file blob as DataURL. If processing.. did you check console for errors? (F12). It sound to me that the canvas is of wrong size (0). Try using width/height instead of naturalWidth/naturalHeight (console.log the canvas size after setting it to check). And your code is a bit messy.. :) –  Dec 02 '16 at 16:36
  • All I needed to do is just convert it to data-URI (base-64) and present it to the user (plus save it to their folder on server as base-64 so they can come back to it later). I'll research this FileReader though, see if that's what I'm looking for. – Aaron Adams Dec 02 '16 at 16:40
  • @AaronAdams [This is a plugin I wrote to do this](https://github.com/Pamblam/fileUpload) There is a [fiddle here](http://jsfiddle.net/7cL08htp/), but it won't work unless you download the fileUpload.js plugin and run it locally since there isn't a CDN for it. – I wrestled a bear once. Dec 02 '16 at 16:41
  • Looks like a lot of work put into that plugin there, looks really nice. Unfortunately, I've vowed with this project (it's a small part of a larger project) not to use jQuery or other plugins... – Aaron Adams Dec 02 '16 at 17:52
  • 1
    To the person trying to edit my post again, the reason I rejected the edit is for two things: First, you tried to change the console output from `data:,` to `data.` which is not what the console outputted. Second, the change from 'refuses' to 'refuse' actually hurts the sentence flow. It's like saying 'Bob eat pizza' instead of 'Bob eats pizza'. The sentence flow also felt weird when you removed all of the commas. Not angry, just explaining why I rejected the edit. – Aaron Adams Dec 02 '16 at 18:01
  • If ever you are still wondering why your first code didn't work, you were setting canvas width and height properties to XXX + 'px', while these params are numeric only. Remove this 'px' string and it will work. – Kaiido Dec 03 '16 at 02:48
  • @Iwrestledabearonce. , not sure what you were exactly saying in your first comment, but two things : dataURI are not always base64 encoded, `data:text/plain;,Hello` is a dataURI, and is not base64 encoded. `URL.createObjectURL(blob)` returns a blobURI (`blob:internalpathtotheblob`) and not a dataURI. It will only be valid for the session time, on ones browser, and can't be shared (except to an other tab of the same UA). But if OP only wanted to display this image, then yes, `createObjectURL` is all he should use. (And I'm still wondering why he wants a dataURI btw). – Kaiido Dec 03 '16 at 03:04
  • @Iwrestledabearonce., Oh and reading further comments, yes you can use a blobURI loaded image and draw it on canvas... what you are saying in this comments stream is just plain wrong... – Kaiido Dec 03 '16 at 03:09
  • @kaiido thank goodness you were there to correct me. – I wrestled a bear once. Dec 03 '16 at 03:14
  • @Kaiido oops, I did accidentally set the canvas width with px... Good catch! I was converting the image to base64 by the way, so the user could save a number of images on the server (in plain text only) and then come back to the same image from a different computer under the same account. – Aaron Adams Dec 03 '16 at 03:54
  • Then don't even convert it to a dataURI, just use the blobURI to display the image and send the blob directly to your server. Do the b64 conversion server side if needed. http://stackoverflow.com/questions/34711715/phpjs-how-to-do-fileuploads-in-html-form-as-content-type-multipart-via-js/34713226#34713226 – Kaiido Dec 03 '16 at 03:58
  • @kaiido a data uri for an image is always base64 afaik. Im not in a posistion to google it atm but feel free to correct me if im wrong. I thank you for your generous invitation but if i delete mine then yours will be out of context and your comment was a valuable contribution. I learned something about blobs and you got to look like a smart ass, so i think ill leave it. – I wrestled a bear once. Dec 03 '16 at 03:58
  • @Iwrestledabearonce., not exactly, it's just that base64 encoding does allow to pass special characters from binary data into URI valid characters, so most binary files are b64 encoded, but if we take the special example of svg images, they're written in plain/text, so they can be simple URLencoded, without the use of base64 (https://css-tricks.com/probably-dont-base64-svg/). – Kaiido Dec 03 '16 at 04:05
  • @Iwrestledabearonce. And for my comments' usefulness, they were only addressed to you, what I said here has already been said somewhere else (a few times by yours), and I didn't wanted to look like a *smart ass*, but just to minimize the effect of a 10K+ SO user on future readers, but I kinda fear they won't go til mine. So I still just prefer you to delete these first misleading ones. – Kaiido Dec 03 '16 at 04:06
  • @kaiido So when the browser sends a server a blob, the server actually recieves the file itself? Didn't know that... All I'd read on server-side was that a `blob:` url couldnt be used between computers... that would come in handy... Also, @Iwrestledabearonce, sorry if I started sounding like that... Didn't mean to sound smarter than I am or anything. By the way, that jQuery plugin was a nice contribution, should be great for jQuery users to find out about! – Aaron Adams Dec 03 '16 at 04:09
  • @AaronAdams, I think the *smart ass* was intended to me, I got a bit strong with the *plain wrong* I used, and I have to tell my apologies about it, but my english is not good enough to find a better wording... For the server-side, yes, your server will get the file directly, but send the `Blob` (or `File` which is just a special Blob), not the BlobURI. => `display : URL.createObjectURL(blob);` `toServer: FormData.append(blob)` – Kaiido Dec 03 '16 at 04:13
  • Ahh. I can literally just send the blob itself to the server, and the browser takes care of making sure the server recieves a file instead. Thanks for that explanation! – Aaron Adams Dec 03 '16 at 04:16

1 Answers1

1

If the intent is to convert the image to Data-URI ("base64"), the FileReader can be used - no need for canvas (which can also alter the image/final compression):

fileIn.onchange = function(e) {
  var fr = new FileReader();
  fr.onload = done.bind(fr);
  fr.readAsDataURL(e.target.files[0]);
}

function done() {
  var dataURI = filePreview.src = this.result;
  alert(dataURI.substr(0, 35) + "...")
}
<input type="file" id="fileIn"><br>
<img id="filePreview"><br>
<canvas id="fileCanvas"></canvas>