1

I'm working on this app where I need to upload image to an html5 canvas from both url as well as user's hard drive. I have the functionality already implemented for the user's input from hard drive but I don't know how to add an image from url.

The code I used to load from input type file are all inside the input's event listener. It has a lot of functions in it as the app requires text input on the image as well. So image from url should have the same functionality as well.

Please note that the imgObj is inside the event listener. I tried changing the img.src to a url to test it out, but even though the image loads, I can't download it anymore. The console throws this error:

Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
    at HTMLButtonElement.<anonymous>

Here's the code minus some functions which is unnecessary to show it here:

const canvas            = document.getElementById('canvas');
const ctx               = canvas.getContext('2d');
const btnDownload       = document.getElementById('btnDownload');
const fileUpload        = document.getElementById('file-upload');

fileUpload.addEventListener('change', function(e) {
    let imgObj          = new Image();
    imgObj.onload       = draw;
    imgObj.onerror      = failed;
    imgObj.src          = URL.createObjectURL(this.files[0]);
    // imgObj.src          = 'https://static.vecteezy.com/system/resources/previews/000/094/478/original/free-abstract-pattern-vector.jpg';
    

    // some functions to write on image

});


function draw() {
    canvas.width        = this.naturalWidth;
    canvas.height       = this.naturalHeight;
    const nw            = this.naturalWidth;
    const nh            = this.naturalHeight;

    ctx.drawImage(this, 0, 0, nw, nh);
};
    
function failed() {
    console.error("The provided file couldn't be loaded as an Image media");
};

btnDownload.addEventListener('click', () => {

    const a = document.createElement('a');
    document.body.appendChild(a);
    a.href = canvas.toDataURL();
    a.download = "canvas-image.png";
    a.click();
    document.body.removeChild(a);

});
<canvas id="canvas" width="800" height="500"></canvas>
<input type="file" id="file-upload" />
<button id="btnDownload">Download</button>

So in short how can I add upload from url functionality by keeping upload from input functionality?

Note that the things inside the input listener are gonna be the same for upload from url functionality. So may be I have to do something with imgObj and img.src.

1 Answer 1

1

You're running into a CORS issue. CORS is a protocol meant to prevent websites from getting access to your data from other sites (cross-origin data). Since images were around since the early days of the web, cross-origin image loading couldn't be blocked due to backward compatibility concerns, but getting access to such data could be blocked.

So when you draw a cross-origin image into a canvas, the canvas gets "tainted", such that there are many things you can no longer do with it, including retrieving its contents as a data URL. Images uploaded from the filesystem are safe, since the user has to opt in.

So in order to use the image data, you have to tell the browser that the image must be loaded in a cross-origin manner. You do that by setting the crossOrigin attribute or property:

<img src="https://static.vecteezy.com/system/resources/previews/000/094/478/original/free-abstract-pattern-vector.jpg" crossorigin>
let imgObj = new Image();
imgObj.src =
      "https://static.vecteezy.com/system/resources/previews/000/094/478/original/free-abstract-pattern-vector.jpg";
imgObj.crossOrigin = "";

With this attribute/property, if the image doesn't support CORS, the load will fail and it won't even display. But if it does, as in this case, you'll be able to get the image data.

Sign up to request clarification or add additional context in comments.

8 Comments

Hey. thanks for the detailed answer. I have been trying this crossOrigin solution for the last half hr but it doesn't seem to work. I get this error every time: Access to image at 'https://static.vecteezy.com/system/resources/previews/000/094/478/original/free-abstract-pattern-vector.jpg' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Hey I can download it now as I used an image url from Imgur. I think they have CORS support. Now can you help me with the event listener issue?
@Zak Huh, that's odd. Access-Control-Allow-Origin is the header servers must include to support CORS. In this case, it seems the server is choosing to accept or reject requests based on which site (and port!) they come from: http://localhost:8000 (as in my tests) is fine, but even http://localhost:8080 gets blocked. Huh.
@Zak No, there isn't. But supporting CORS is a best practice, so if you find some site that doesn't support it for things like images, consider contacting them.
@Zak By the way, there are also CORS proxies that allow you to access a site as if it supported CORS. This doesn't run into the "getting access to your data" problem because those proxies don't have your cookies.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.