【问题标题】:Can't download canvas content无法下载画布内容
【发布时间】:2017-05-15 13:25:41
【问题描述】:

所以我尝试使用电子构建一个应用程序,该程序非常基本,用户应该上传几张图片,他们按下一个按钮,我将它们绘制到 <canvas> 元素中,他们应该能够下载它,我遇到的问题是,当我尝试制作可下载按钮时,会引发安全错误,我做了一些研究,发现它是 HTML5 的一个功能,我认为我无法添加 crossOrigin = '匿名'因为用户要从自己的电脑上加载图片,所以我的问题是......

a)- 有没有办法“欺骗系统”并能够下载画布的内容?,

b)- 如果 (a) 不可行...是否有替代方法可以与画布元素类似地工作?

我已经在使用 FileReader() 来获取用户选择的图片,并且我已经将其绘制到画布中,它看起来很棒,我遇到的问题是我无法下载我为那个安全错误创建了。 .toDataURL 不能被调用,因为那是引发错误的那个。

从输入中读取图片的代码:

 function handleBox1(evt) {
    var files = evt.target.files; // FileList object

    for (var i = 0, f; f = files[i]; i++) {
      // Only process image files.
      if (!f.type.match('image.*')) {
        continue;
      }
      var reader = new FileReader();

      reader.onload = (function(theFile) {
        return function(e) {

            $('#imgBox1').attr("src",e.target.result);              

        };
      })(f);
      // Read in the image file as a data URL.
      reader.readAsDataURL(f);
    }
}

这是一个简单的函数,可以查看是否可以从画布中获取 Url:

descargar = function(){
  var w=window.open('about:blank','image from canvas');
  w.document.write("<img src='"+ totem.toDataURL("image/png")+ "' alt='from canvas'/>");
}

【问题讨论】:

  • 你能发布你的代码和错误吗?是的跨域根本与用户上传无关。由第三方网站或链接引起。
  • “...欺骗系统”:不,满足安全要求:是。如果用户拥有现代浏览器,您可以使用 FileReader 获取本地图像而不会触发安全限制。关键是用户必须明确选择所需的文件,从而明确承担上传跨域图像的责任。
  • @markE 链接的问题没有出现在单个 &lt;canvas&gt; 元素上绘制多个图像?
  • @markE 是的,FileReader() 是返回预期结果的重要部分。如果没记错,&lt;canvas&gt; 元素不会出现在链接问题的html 处?在绘制到canvas 之前,不一定“容易”地添加和减去图像高度;比最初预期的要困难得多。特别是,使用canvas.toDataURL() 其中一张图像具有大量size 可能会冻结浏览器,因为data URI 被写入a.href;使用canvas.toBlob() polyfill 不会导致浏览器冻结,即使方法实际上调用了.toDataURL()
  • “在问题中,Gabriel Tortomano 说他们的应用需要“对宽度最大的图像进行排序,将该图像宽度添加到下一个图像宽度,然后在绘制最后一个图像之前减去最后一个图像宽度” ?" 在尝试解决问题时,如果图像具有不同的宽度,如果不使用具有最大width 的图像来设置canvas width,则画布可以使用剪切图像进行渲染。虽然FileReader() 用于链接问题,但链接问题不要求将两个图像合并到同一个canvas。试图解决当前的问题;甚至没有尝试使用三张图片

标签: javascript html canvas


【解决方案1】:

用户应该上传几张图片,我把它们画成一个 元素,他们应该能够下载它

您可以利用包含 IIFE 的 for 循环来处理来自 input type="file" 元素的 files 对象、new Imageonload 事件的 &lt;img&gt; 元素、URL.createObjectURL()Array.prototype.sort() 以确定最大 @上传的两张图片中的987654331@; Promise.all(), Array.prototype.forEach() 处理img 元素; canvas.toBlob() polyfill 创建一个objectURL 的图像; &lt;a&gt; 元素将download 属性设置为objectURL,表示从两个图像创建的单个图像;图像下载完成后,在 a 元素的 click 处撤销 objectURL

// if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
  value: function(callback, type, quality) {

    var binStr = atob(this.toDataURL(type, quality).split(',')[1]),
      len = binStr.length,
      arr = new Uint8Array(len);

    for (var i = 0; i < len; i++) {
      arr[i] = binStr.charCodeAt(i);
    }

    callback(new Blob([arr], {
      type: type || 'image/png'
    }));
  }
});
//}

var input = document.querySelector("input");
var canvas = document.querySelector("canvas");
var a = document.querySelector("a");
var ctx = canvas.getContext("2d");
var urls = [];

input.onchange = function(e) {
  var images = [];

  for (var i = 0; i < e.target.files.length; i++) {
    (function(j) {
      // var reader = new FileReader();
      // reader.onload = function(event) {
        var img = new Image;
        img.onload = function() {
          images.push(Promise.resolve(this));
          if (images.length === e.target.files.length) {
            Promise.all(images)
              .then(function(data) {
                data.sort(function(a, b) {
                  return a.naturalHeight < b.naturalHeight;
                })
                data.forEach(function(el, index) {
                  if (index === 0) {
                    canvas.width = el.naturalWidth;
                    canvas.height = el.naturalHeight 
                                    + data[index + 1].naturalHeight;
                    ctx.drawImage(el, 0, 0);
                  } else {
                    ctx.drawImage(el, 0, canvas.height - el.naturalHeight);
                  }

                });
                canvas.toBlob(function(blob) {
                  var url = URL.createObjectURL(blob);
                  console.log(blob);
                  a.href = url;
                  a.style.display = "block";
                  urls.push(url);
                });
              })
          }
        }
        img.src = URL.createObjectURL(e.target.files[j]);
      // }
      // reader.readAsDataURL(e.target.files[j]);
    }(i))
  }
}

a.onclick = function() {
  setTimeout(function() {
    urls.forEach(function(obj) {
      URL.revokeObjectURL(obj)
    })
  })
}
<a download="" href="" style="display:none">download</a>
<input type="file" accept="image/*" multiple />
<br>
<canvas></canvas>

【讨论】:

  • 您的代码在 IE 和 Edge 中失败,因为这些浏览器不支持 download 属性。但是赞成使用 FileReader 来满足跨域限制。
  • 究竟为什么这里的 FileReader 过于复杂?你已经依赖了 URL 的存在,所以一直使用它,它会去除一层嵌套。
  • @Kaiido 你的意思是用.toBlob()吗?
  • 不,我的意思是(function(j) { var img =new Image(); img.onload = ... ; img.src = URL.createObjectURL(e.target.files[j])})(i);,即不要在此处使用 FileReader,它没用,会使您的代码过于复杂并占用不必要的内存(createObjectURL 使用用户输入的文件调用只会创建指向文件的直接指针在用户的磁盘上)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-10
  • 2016-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-06
相关资源
最近更新 更多