使用JSZip将图片链接转换为base64并添加到zip文件中

3

基本上我想将几张图片添加到zip文件中并能够下载它。我有两个变量分别保存图像的来源-pic1, pic2和一个全局变量basePic来保存base64字符串。我有一个将图像转换为base64的函数:

var pic; //http://www.somesite.com/i/picture.jpg
var pic2; //http://www.somesite.com/i/picture2.jpg
var basePic;// base64 string of an image

function getBase64FromImageUrl(url) {
    var img = new Image();
    img.setAttribute('crossOrigin', 'anonymous');

    img.onload = function () {
    var canvas = document.createElement("canvas");
    canvas.width =this.width;
    canvas.height =this.height;

    var ctx = canvas.getContext("2d");
    ctx.drawImage(this, 0, 0);

    var dataURL = canvas.toDataURL("image/png");
    basePic=dataURL.replace(/^data:image\/(png|jpg);base64,/, "");  
    }; 

    img.src = url; // I was trying to put this after setAttribute, but it's the same effect
    }

稍后我会尝试创建一个zip文件,并像这样调用该函数:
var zip = new JSZip();

getBase64FromImageUrl(pic);
alert("random text"); // without it, it's just empty file added to zip
zip.file("picture.png",basePic, {base64: true});

getBase64FromImageUrl(pic2);
alert("random text");
zip.file("picture2.png",basePic, {base64: true});

zip.generateAsync({type:"blob"})
.then(function(content) {
saveAs(content, "example.zip");
});

这个函数可以使用一张图片,但是如果有多张图片,它就变得更加随机。如果我在不刷新页面的情况下运行代码几次,可能会得到我需要的结果。但大多数时候,在我的 zip 文件中只有第二张图片出现了两次,而且我必须在调用该函数后alert一些东西,因为没有basePic将为空。我想修复此函数,以使其能够轻松地将多个图像源转换为 base64 字符串。

1.如何使此函数按预期工作,输入 url -> 输出 base64,同时处理多个图像?

2.如何在每次调用该函数时无需警报某些内容?

1个回答

6
问题在于压缩进程在图像加载前开始。将文件添加到zip归档的过程必须从onload处理程序内部开始。
然而,这里还有几件事情需要考虑:
- 如果要将图像直接添加到存档中,则无需通过画布进行转换。 - 转换为和从数据URI都非常昂贵并且消耗内存。JSZip在内部使用pako_delate,它与类型化数组视图(Uint8Array)配合得更好。
我建议使用以下方法:
- 在应用程序开始时(或在开始加载文件之前)创建zip对象。 - 使用XMLHttpRequest加载每个文件,并将内容请求为ArrayBuffer。这将允许您直接将任何类型的文件加载到二进制格式中,从而提高性能和资源。 - 随着文件的到来逐个添加。 - 当所有URL都传递时,调用generateAsync()方法,然后就完成了。
示例: 这不是一个生产就绪的例子,也不打算成为,但它将显示主要步骤。根据您的情况进行调整,在适当的位置添加错误处理和健全性检查。

// create archive before loading process begin
var zip = new JSZip();

// image list to add:
var images = [
      "//i.imgur.com/qDFCWVnb.jpg",
      "//i.imgur.com/VBvzEq1b.jpg",
      "//i.imgur.com/ZWtUEuLb.jpg"
    ],
    index = 0;  // for loader

// function to load a single image as arraybuffer using XMLHttpRequests
// it will assume cross-origin usage is allowed
function loadAsArrayBuffer(url, callback) {
  var xhr = new XMLHttpRequest();
  xhr.open("GET", url);
  xhr.responseType = "arraybuffer";
  xhr.onerror = function() {/* handle errors*/};
  xhr.onload = function() {
    if (xhr.status === 200) {callback(xhr.response, url)}
    else {/* handle errors*/}
  };
  xhr.send();
}

// loading process. Here it will load one and one image.
// You can modify it to load several at once but some browsers put
// a cap on how many XHR connections can be open at the same time..
(function load() {
  if (index < images.length) {
    loadAsArrayBuffer(images[index++], function(buffer, url) {
      var filename = getFilename(url);
      zip.file(filename, buffer); // image has loaded, add it to archive
      load();                     // load next image
    })
  }
  else {                          // done! present archive somehow
    zip.generateAsync({type:"blob"}).then(function(content) {
      // save out
      lnk.href = (URL || webkitURL).createObjectURL(content);
      lnk.innerHTML = "Right-click + Save as to download zip file";
      lnk.download = "DemoZip.zip";
    });
  }
})();

// Just for this demo! keep separate array with filename or
// modify to allow for "filename-less" uris.
function getFilename(url) {
  return url.substr(url.lastIndexOf("/") + 1)
}
<script src="https://raw.githubusercontent.com/Stuk/jszip/master/dist/jszip.min.js"></script>
Loading external lib + images... hold on...
<br><a id=lnk></a>


在我的例子中,主要问题在于将图像转换为base64的函数,甚至在添加到zip之前。我有点希望修复那个函数,但我会尝试您的解决方案,看看它是否能够工作。 - norbidrak
谢谢,很好用,即使使用 AJAX 和更多的图片也没有问题,但是在保存文件之前我仍然需要进行一次 'alert()' 调用,以便获取正确数量的对象来压缩。我猜这是一个异步的问题。这是一个重大的改进,但是有没有想过如何模拟 'alert()',让编译器在尝试制作 zip 文件之前等待所有对象? - norbidrak
@markE抱歉让您失望了,但是不,这与另一个事情有关(当时已经声明过)。 :) - user1693593

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接