0

First time posting, this is the whole code (majority of it I found online and tweaked some things to serve my purpose), but more specifically, my error is towards the end where I get.

Uncaught TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed.

When I simply use saveAs(img_url, "img.png"), the option to save to laptop pops up. But I get the error I mentioned above when trying to use "content". I have filesaver and jszip in the script, I just can't seem to find any way to fix the error, which then stops executing anything more. Sorry for messy code, would really appreciate help.

Main part is towards the bottom, the rest is there just incase someone might want to see. Theres the url to blob then the canvas generator, I just don't know why it won't save.

!function() {
    function dataURLtoBlob(dataURL, type) {
      var binary = atob(dataURL.split(',')[1]),
          length = binary.length,
          binaryArray = new Uint8Array(length);
      for (var i = 0; i < length; i++) {
        binaryArray[i] = binary.charCodeAt(i);
      }
      return new Blob([binaryArray], {type: type});
    }

    var SolidImage = function() {
      var canvas = document.createElement('canvas'),
          ctx = canvas.getContext('2d');
      this.img = new Image();
      this.make = function(color) {
        canvas.width = 500;
        canvas.height = 500;
        
        ctx.fillStyle = color;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = "#FFFFFF";
        ctx.textAlign = "center";
        ctx.font = "bold 50px Courier New";
        ctx.fillText(color.substring(3), 250, 250);
        var dataURL = canvas.toDataURL('image/png')
        this.img.src = dataURL;
        if (this.blobURL) URL.revokeObjectURL(this.blobURL);
        this.blob = dataURLtoBlob(dataURL, 'image/png');
        this.blobURL = URL.createObjectURL(this.blob);
      }
    };
    
    var solidImage = new SolidImage(),
        button = document.getElementById('make'),
        result = document.getElementById('result'),
        link = document.createElement('a');
    
    link.setAttribute('target', '_blank');
    result.appendChild(solidImage.img);
    result.insertAdjacentHTML('beforeend', 'Save this image or<br>');
    result.appendChild(link);
    solidImage.img.width = 600;
  
    
    button.addEventListener('click', function(){
        var zip = new JSZip();
        console.log("after zip");
        //var img = zip.folder("rdm_imgs");
        //////////////////////////////////
        for (var i = 0; i < 1; i++) {
            setTimeout(function() {
        var rgb_r = Math.floor(Math.random() * (256+1)),
            rgb_g = Math.floor(Math.random() * (256+1)),
            rgb_b = Math.floor(Math.random() * (256+1)),
            random_color = "rgb(" + rgb_r + ", " + rgb_b + ", " + rgb_g + ")";
      var filename = random_color.replace(/\s/g, "") + '.png';
      solidImage.make(random_color);
      link.innerHTML = 'Download content ' + filename;
      var img_url = solidImage.blob;
      //console.log(img_url.replace(/^data:image\/(png|jpg);base64,/, ""));
      console.log(img_url);
      //link.setAttribute('href', img_url);
      //link.setAttribute('download', filename);
      result.className = 'generated';

      zip.file(filename, img_url);
            },i * 500)}
        console.log("after loop");
        var content = zip.generateAsync({type:"blob"});
        console.log("after zip generate");
        saveAs(content, "imgs.zip");
        console.log("after saveAs");
        //link.innerHTML = 'Download Contents.zip';
        //var img_url = solidImage.blobURL;
        //link.setAttribute('href', content);
        //link.setAttribute('download', "content.zip");
    });
  }();
absolutenoob
  • 23
  • 1
  • 7
  • Where is solid image from? – Invizi Nov 21 '21 at 22:03
  • @Invizi theres a small html portion with a button (didnt code that) which runs when clicked and solid image is a canvas...and i just now noticed it was not added to my question, editing it in right now – absolutenoob Nov 21 '21 at 23:34

1 Answers1

2

zip.gzip.generateAsync() returns a Promise. This Promise will resolve with a Blob, some times later, but it's a Promise, not a Blob.
So you need to await the resolution of this Promise to access the generated Blob.

You can either mark your function as async and then use the await keyword:

button.addEventListener('click', async function(){
  // ...
  var content = await zip.generateAsync({type:"blob"});

Or wrap the saveAs part in a callback passed to the Promise's .then():

zip.generateAsync({type:"blob"}).then(function(content) {
  console.log("after zip generate");
  saveAs(content, "imgs.zip");
})

Now, whatever you choose, your zip file will actually be empty. You add content to it only in the callback of setTimeout, meaning that this content will be added only after you did create the zip file, which is too late.
So remove the setTimeout( part which seems useless and execute its callback's content directly.

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • I had used the setTimeout to add a delay when visually seeing it change through random colours on the HTML page. I will try that, although how come the zip would be empty? But I would assume that since I initialize the zip as soon as button is clicked, then within the loop I'm adding files to zip folder? – absolutenoob Nov 21 '21 at 23:39
  • Wow, this worked, thank you very much. If you could, could you maybe explain why specifically that was the issue? – absolutenoob Nov 21 '21 at 23:46
  • The issue causing "Uncaught TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed." was that you passed a Promise object instead of a Blob. For the empty zip it's because `setTimeout(fn)` will delay `fn` for some time later (even if the timeout is `0`). So when this callback `fn` is called, the lines below will already have been executed. And since in the lines below you do finalize the zip file, this zip file is being created before you added any file to it, i.e it's empty. – Kaiido Nov 22 '21 at 00:46
  • oh, so setTimeout(fn) pushes fn to execute last? Interesting, then how would I make a for loop that has a delay between each iteration, but also not postpone the calls to the end? – absolutenoob Nov 22 '21 at 20:28
  • Use Promises, you can have a look at https://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call – Kaiido Nov 22 '21 at 22:37