为什么 FileSaver saveAs 不能与 JSZip 一起使用?

Why FileSaver saveAs won't work with a JSZip?

第一次发帖,这是完整的代码(其中大部分是我在网上找到的,并调整了一些东西以达到我的目的),但更具体地说,我的错误是在接近尾声的地方。

未捕获类型错误:无法在 'URL' 上执行 'createObjectURL':重载解析失败。

当我简单地使用 saveAs(img_url, "img.png") 时,弹出保存到笔记本电脑的选项。但是我在尝试使用“内容”时遇到了上面提到的错误。我在脚本中有 filesaver 和 jszip,我似乎找不到任何方法来修复错误,然后停止执行更多。抱歉代码混乱,非常感谢帮助。

主要部分在底部,其余部分都在那里,以防有人想看。有 url 到 blob 然后 canvas 生成器,我只是不知道为什么它不会保存。

!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");
    });
  }();

zip.gzip.generateAsync()returns一个Promise。这个 Promise 会在一段时间后用 Blob 解析,但这是一个 Promise,而不是 Blob。
因此,您需要等待此 Promise 的解析才能访问生成的 Blob。

您可以将函数标记为 async,然后使用 await 关键字:

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

或者将 saveAs 部分包装在传递给 Promise 的回调中 .then():

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

现在,无论您选择什么,您的 zip 文件实际上都是空的。您仅在 setTimeout 的回调中向其添加内容,这意味着此内容只会在您创建 zip 文件后添加,但为时已晚。
所以把看起来没用的setTimeout(部分去掉,直接执行回调的内容。