Search code examples
javascriptcanvashtml5-canvasblobfileapi

Javascript convert image to File return zeros


I have img from other server

<img src="https://cloudserver/1.jpg">

On click this img i want convert to File object for send post request to server. After this function i see that data all bytes = 0, dont understand what happing ? In search i find problem about that need wait then image is loading done but i 100% have already loaded image.

imgToFile: function (imageElement){
    imageElement.crossOrigin="anonymous";

    var canvasElement = document.createElement("canvas");
    canvasElement.width = imageElement.width;
    canvasElement.height = imageElement.height;
    var canvasContext = canvasElement.getContext("2d");
    canvasContext.drawImage(imageElement, 0, 0);

    var imageData = canvasContext.getImageData(0, 0, imageElement.width, imageElement.height).data;
    var buffer = new Uint8Array(imageData.length);
    for(var index = 0; index < imageData.length; index++)
        buffer[index] = imageData[index];

    var imageBlob = new Blob(buffer);
    return new File([imageBlob], /\/([^/]+)$/.exec(imageElement.src)[1]);
}

Sandbox: https://liveweave.com/DCXuNY


Solution

  • That's quite surprising to me who thought I knew this part of the specs, but turns out that since 2016, changing the crossorigin attribute state is part of the img's relevant mutations, which will force a refetching of the image source.

    Chrome only caught up with the specs relatively recently (M84), Firefox still hasn't and will thus just throw an Error complaining about the fact the image did taint the canvas.

    Chrome on the other hand will thus refetch the image as soon as you change that crossOrigin IDL attribute, this time with the proper CORS headers. So at the time you hit drawImage in that browser, it still won't have fetched the CORS compliant resource (even though it's the same file, because it's served with different headers, browsers will fetch it entirely again).

    So contrarily to what you thought, your image is still not loaded in Chrome.

    To fix that, you could add an onload event handler in your js script, but you'd also need to force the refetching of the image for other browsers by setting again the src attribute (even to the same value):

    onload = (evt) => {
      const img = document.querySelector( 'img' );
      img.onload = (evt) => {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        ctx.drawImage(img,0,0);
        console.log(...ctx.getImageData(250,120,1,1).data);
      };
      img.crossOrigin = 'anonymous';
      img.src = img.src; // force refetching for non-Chrome browsers
    };
    img { width: 300px }
    <img src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png">

    But the best is certainly to request your image directly with the proper CORS headers, from the beginning, so you don't make your users download two times the same file for nothing.

    onload = (evt) => {
      const img = document.querySelector( 'img' );
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      ctx.drawImage(img,0,0);
      console.log(...ctx.getImageData(250,120,1,1).data);
    };
    img { width: 300px }
    <img src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" crossorigin="anonymous">