32

My goal is to have users on iPad load an image into a canvas, then get the base 64 encoded said image all while being OFFLINE

JSFiddle

JSFiddle

Code

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="../js/libs/jquery-1.7.1.min.js"></script>
    <script>
      $(document).ready(function(){ 
        //Get the canvas
        var canvas = document.getElementById('testCanvas');
        var context = canvas.getContext('2d');   

        $("#testButton").click(function(){
          var base_image = new Image();

          base_image.src = $("#testImg").val();
          //base_image.src = '1.jpg';

          //When the image loads
          $(base_image).load(function(){
            //Resize canvas for image
            $("#testCanvas").attr({
              width: base_image.width,
              height: base_image.height
            });

            //Draw image on canvas
            context.drawImage(base_image, 0, 0);

            //Get base64 encoded image
            var imageString = canvas.toDataURL("image/jpeg");
            $("#imageString").val(imageString);

            //alert($("#imageString").val().length);
            $("#imageOutput").attr("src", imageString);
          });//image load
        });//Test Button Click
      });//doc ready
    </script>
  </head>

  <body>
    <form>
      <input type="file" name="testImg" id="testImg" />
    </form>
    <button id="testButton">Test</button>
    <canvas id="testCanvas" style="border: 1px solid black;">Your Browser does not support canvas</canvas>
    <br />
    <fieldset>
      <legend>Image Data</legend>
      <textarea id="imageString"></textarea>

      <img id="imageOutput" src="" />
    </fieldset>
  </body>
</html>

I know the image isn't actually loaded from the <input type='file' />, but I figured it was worth a shot. In Chrome console I get:

Not allowed to load local resource

Is there any way for me to get images from my iPad into a canvas element?

Any help, tips or advice is greatly appreciated! Thanks!

FunkyMonk91
  • 1,469
  • 1
  • 17
  • 30

2 Answers2

77

I have a functioning fiddle (based on the prior work of this answer) that demonstrates how to upload an image using a file input, place it inside a canvas, and read the base64 data URL back out.

In short, you should:

  1. Use the File API to read in the image (you might do this in an onchange listener of the input element):
var file = input.files[0];
var fr = new FileReader();
fr.onload = createImage;   // onload fires after reading is complete
fr.readAsDataURL(file);    // begin reading
  1. In your FileReader's onload callback (here, createImage), read the result of the FileReader (here, fr.result). That's your image data URL!

OPTIONAL STEPS (only needed if you plan to manipulate the images on a canvas):

  1. In your FileReader's onload callback (here, createImage), make a new Image object and set its src to the result of the FileReader:
img = new Image();
img.onload = imageLoaded;
img.src = fr.result;
  1. Finally, in your Image's onload callback, draw it to the canvas and then use canvas.toDataUrl to the data:
canvas.width = img.width;      // set canvas size big enough for the image
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img,0,0);         // draw the image

// do some manipulations...

canvas.toDataURL("image/png");  // get the data URL
Yves M.
  • 29,855
  • 23
  • 108
  • 144
apsillers
  • 112,806
  • 17
  • 235
  • 239
  • `fr.readAsDataURL` will set `result` to a data URL so there isn't a need to go through `new Image` and `` unless you're planning to edit the image in canvas to get a different output. – Paul S. Dec 18 '12 at 18:33
  • 1
    @PaulS. Right, I guess this goes a bit too far! I only did the `canvas` stuff because it was in the OP's original question -- I'll move the extraneous parts to a different section, just in case the OP does want to edit the image after all. – apsillers Dec 18 '12 at 18:47
  • Thank you for the good answer. I tried it on Safari but it does not work. Do you have any tips on that? http://stackoverflow.com/questions/27860782/file-api-not-supported-on-safari-browser – AMG Jan 09 '15 at 12:42
  • @AMG If you're using readAsDataURL you don't need an onload callback on the image. As it is loaded immediately. In fact, I found in Firefox that if I had a callback it actually doesn't work. – PhilT Mar 16 '15 at 16:49
  • I got this message: `Refused to load the image 'data:image/jpeg;base64,/9j/4AAQSkZ...2Sv/Z' because it violates the following Content Security Policy directive: "default-src 'self' 'unsafe-inline'". Note that 'img-src' was not explicitly set, so 'default-src' is used as a fallback. ` – Grim May 05 '21 at 16:58
  • @Grim Your site is served with a Content Security Policy and you need to add `data:` to your `default-src` or (much better) add an `img-src` directive with `data:` (plus any other things you use from`default-src` like `'self:`) – apsillers May 05 '21 at 18:03
  • @Grim consider the implications of adding the data: scheme to the CSP before jumping on that solution: https://security.stackexchange.com/questions/94993/is-including-the-data-scheme-in-your-content-security-policy-safe/95011#95011 – teleclimber Jul 08 '21 at 19:10
3

input file, reduce file size with javascript after file upload and pass input file value again.

const input = document.getElementById("img-input");
input.onchange = function (ev) {
  const file    = ev.target.files[0]; // get the file
  const blobURL = URL.createObjectURL(file);
  const img     = new Image();
  img.src       = blobURL;

  img.onerror = function () {
    URL.revokeObjectURL(this.src);
    // Handle the failure properly
    console.log("Cannot load image");
  };

  img.onload = function () {
    URL.revokeObjectURL(this.src);
    const mime_type = "image/jpeg";
    const quality = qualityRate(file.size);
    const canvas  = document.createElement("canvas");
    canvas.width  = img.width;
    canvas.height = img.height;
    const ctx     = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0, img.width, img.height);

    document.getElementById("root").append(canvas);
    //let data = canvas.toDataURL(mime_type,quality);
    //downloadURI(data, 'stage.jpg');

    canvas.toBlob(blob => {
        let localfile = new File([blob], 'a.jpg',{type:"image/jpeg", lastModified:new Date().getTime()}, 'utf-8');
        let container = new DataTransfer();
        container.items.add(localfile);
        document.querySelector('#img-input').files = container.files;
    },mime_type,quality);
  };
};

function qualityRate(fileSize){
    let QUALITY = 0.5;
    
    if(1000000 > fileSize){
        QUALITY = 0.5;
    }
    else if(3000000 > fileSize){
        QUALITY = 0.7;
    }
    else if(5000000 > fileSize){
        QUALITY = 0.5;
    }
    else if(10000000 > fileSize){
        QUALITY = 0.9;
    }
    else{
        QUALITY = 0.1;
    }
    console.log(QUALITY);
    return QUALITY;
}

function downloadURI(uri, name) {
    var link = document.createElement('a');
    link.download = name;
    link.href = uri;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    delete link;
}
<form action="test.php" method="post" enctype="multipart/form-data">
    <input id="img-input" type="file" name="fileTest" accept="image/*" />
    <input type="submit" name="submit" value="aa">
</form>
<div id="root" style=" display: none; "></div>