【问题标题】:Get image data URL in JavaScript?在 JavaScript 中获取图像数据 URL?
【发布时间】:2010-10-30 08:37:49
【问题描述】:

我有一个带有一些图像的常规 HTML 页面(只是常规的<img /> HTML 标签)。我想获取他们的内容,最好是 base64 编码,而不需要重新下载图像(即它已经被浏览器加载,所以现在我想要内容)。

我很想用 Greasemonkey 和 Firefox 来实现。

【问题讨论】:

    标签: javascript image firefox greasemonkey base64


    【解决方案1】:

    这就是你需要阅读的全部内容。

    https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString

    var height = 200;
    var width  = 200;
    
    canvas.width  = width;
    canvas.height = height;
    
    var ctx = canvas.getContext('2d');
    
    ctx.strokeStyle = '#090';
    ctx.beginPath();
    ctx.arc(width/2, height/2, width/2 - width/10, 0, Math.PI*2);
    ctx.stroke();
    
    canvas.toBlob(function (blob) {
      //consider blob is your file object
    
      var reader = new FileReader();
    
      reader.onload = function () {
        console.log(reader.result);
      }
    
      reader.readAsBinaryString(blob);
    });
    

    【讨论】:

      【解决方案2】:

      使用 fetch 的 kaiido 答案的更现代版本是:

      function toObjectUrl(url) {
        return fetch(url)
            .then((response)=> {
              return response.blob();
            })
            .then(blob=> {
              return URL.createObjectURL(blob);
            });
      }
      

      https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

      编辑:正如 cmets 中所指出的,这将返回一个指向本地系统中文件的对象 url,而不是实际的 DataURL,因此根据您的用例,这可能不是您所需要的。

      您可以查看以下答案以使用 fetch 和实际的 dataURL:https://stackoverflow.com/a/50463054/599602

      【讨论】:

      • 如果您有一个使用此方法长时间运行的应用程序,请不要忘记致电URL.revokeObjectURL(),否则您的内存会变得混乱。
      • 注意:DataURL(base64 编码)与 ObjectURL(引用 blob)不同
      • ObjectURL 肯定不是数据 URL。这不是一个正确的答案。
      【解决方案3】:

      shiv / shim / sham

      如果您的图片已经加载(或未加载),这个“工具”可能会派上用场:

      Object.defineProperty
      (
          HTMLImageElement.prototype,'toDataURL',
          {enumerable:false,configurable:false,writable:false,value:function(m,q)
          {
              let c=document.createElement('canvas');
              c.width=this.naturalWidth; c.height=this.naturalHeight;
              c.getContext('2d').drawImage(this,0,0); return c.toDataURL(m,q);
          }}
      );
      

      .. 但是为什么呢?

      这具有使用“已经加载”的图像数据的优势,因此不需要额外的请求。此外,它让最终用户(像您这样的程序员)决定 CORS 和/或 mime-typequality -或者-您可以省略这些参数/参数,如 中所述MDN 规范here.

      如果您已经加载了这个 JS(在需要之前),那么转换为 dataURL 就像这样简单:

      例子

      HTML
      <img src="/yo.jpg" onload="console.log(this.toDataURL('image/jpeg'))">
      
      JS
      console.log(document.getElementById("someImgID").toDataURL());
      

      GPU 指纹识别

      如果您担心位的“精确性”,那么您可以根据@Kaiido 的回答提供的需要更改此工具。

      【讨论】:

      • 酷!!!太有用了。这应该被投票给顶部。
      • 优秀的方法!说得好。
      【解决方案4】:

      加载后使用onload事件转换图片

      function loaded(img) {
        let c = document.createElement('canvas')
        c.getContext('2d').drawImage(img, 0, 0)
        msg.innerText= c.toDataURL();
      }
      pre { word-wrap: break-word; width: 500px; white-space: pre-wrap; }
      <img onload="loaded(this)" src="https://cors-anywhere.herokuapp.com/http://lorempixel.com/200/140" crossorigin="anonymous"/>
      
      <pre id="msg"></pre>

      【讨论】:

        【解决方案5】:

        在 HTML5 中最好使用这个:

        {
        //...
        canvas.width = img.naturalWidth; //img.width;
        canvas.height = img.naturalHeight; //img.height;
        //...
        }
        

        【讨论】:

        • 在回答问题时尝试更具体并提供详细的解释。
        • 感谢您指出这一点!我总是在不知道这一点的情况下得到裁剪的图像。
        【解决方案6】:

        很久以后,但这里没有一个答案是完全正确的。

        在画布上绘制时,传递的图像是未压缩的 + 全部是预乘的。
        导出时,其未压缩或使用不同的算法重新压缩,并且未相乘。

        所有浏览器和设备在此过程中都会发生不同的舍入错误
        (见Canvas fingerprinting)。

        因此,如果想要一个图像文件的 base64 版本,他们必须再次请求它(大部分时间它将来自缓存)但这次是作为 Blob。

        然后您可以使用 FileReader 将其作为 ArrayBuffer 或 dataURL 读取。

        function toDataURL(url, callback){
            var xhr = new XMLHttpRequest();
            xhr.open('get', url);
            xhr.responseType = 'blob';
            xhr.onload = function(){
              var fr = new FileReader();
            
              fr.onload = function(){
                callback(this.result);
              };
            
              fr.readAsDataURL(xhr.response); // async call
            };
            
            xhr.send();
        }
        
        toDataURL(myImage.src, function(dataURL){
          result.src = dataURL;
        
          // now just to show that passing to a canvas doesn't hold the same results
          var canvas = document.createElement('canvas');
          canvas.width = myImage.naturalWidth;
          canvas.height = myImage.naturalHeight;
          canvas.getContext('2d').drawImage(myImage, 0,0);
        
          console.log(canvas.toDataURL() === dataURL); // false - not same data
          });
        <img id="myImage" src="https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png" crossOrigin="anonymous">
        <img id="result">

        【讨论】:

        • 刚刚经历了超过 5 个不同的问题,您的代码是唯一可以正常工作的,谢谢 :)
        • 我在上面的代码中得到了这个错误。 XMLHttpRequest cannot load data:image/jpeg;base64,/9j/4AA ... /2Q==. Invalid response. Origin 'https://example.com' is therefore not allowed access.
        • @STWilson,是的,这种方法也与同源策略相关,就像画布一样。如果是跨域请求,您需要配置托管服务器以允许对您的脚本进行此类跨域请求。
        • @Kaiido 所以如果图像在另一台服务器上而不是托管服务器必须启用跨源请求的脚本?如果您设置 img.setAttribute('crossOrigin', 'anonymous') 是否可以防止错误?
        • 经过更多研究,看起来图像可以使用 crossOrigin 属性来请求访问,但由托管服务器通过 CORS 标头sitepoint.com/an-in-depth-look-at-cors 提供访问权限。至于 XMLHttpRequest,看起来服务器必须通过 CORS 标头提供与图像相同的访问权限,但您的代码不需要更改,除非将 xhr.withCredentials 设置为 false(默认值)。
        【解决方案7】:

        注意:这仅适用于图片来自与页面相同的域,或者具有crossOrigin="anonymous" 属性并且服务器支持 CORS。它也不会给你原始文件,而是一个重新编码的版本。如果您需要结果与原始结果相同,请参阅Kaiido's answer


        您将需要创建具有正确尺寸的画布元素并使用drawImage 函数复制图像数据。然后您可以使用toDataURL 函数获取具有base-64 编码图像的data: url。请注意,图像必须完全加载,否则您将返回一个空的(黑色、透明)图像。

        应该是这样的。我从未编写过 Greasemonkey 脚本,因此您可能需要调整代码以在该环境中运行。

        function getBase64Image(img) {
            // Create an empty canvas element
            var canvas = document.createElement("canvas");
            canvas.width = img.width;
            canvas.height = img.height;
        
            // Copy the image contents to the canvas
            var ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0);
        
            // Get the data-URL formatted image
            // Firefox supports PNG and JPEG. You could check img.src to
            // guess the original format, but be aware the using "image/jpg"
            // will re-encode the image.
            var dataURL = canvas.toDataURL("image/png");
        
            return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
        }
        

        获取 JPEG 格式的图像不适用于旧版本(大约 3.5)的 Firefox,因此如果您想支持它,您需要检查兼容性。如果不支持编码,则默认为“image/png”。

        【讨论】:

        • 虽然这似乎有效(返回中的未转义 / 除外),但在对使用 file_get_contents 获得的文件执行 base64_encode 时,它​​不会创建与我从 PHP 获得的相同的 base64 字符串功能。这些图像看起来非常相似/相同,但 Javascripted 的图像仍然更小,我希望它们完全相同。还有一件事:输入图像是一个小的(594 字节)、28x30 透明背景的 PNG——如果这有什么改变的话。
        • Firefox 可能使用了不同的压缩级别,这会影响编码。另外,我认为 PNG 支持一些额外的标题信息,如注释,这些信息会丢失,因为画布只获取像素数据。如果您需要它完全相同,您可以使用 AJAX 获取文件并手动对其进行 base64 编码。
        • 是的,您将使用 XMLHttpRequest 下载图像。希望它会使用图像的缓存版本,但这取决于服务器和浏览器配置,并且您将有请求开销来确定文件是否已更改。这就是为什么我一开始不建议这样做的原因 :-) 不幸的是,据我所知,这是获取原始文件的唯一方法。
        • @trusktr drawImage 什么都不做,你最终会得到一个空白画布和生成的图像。
        • @JohnSewell 这只是因为 OP 想要内容,而不是 URL。如果要将其用作图像源,则需要跳过 replace(...) 调用。
        【解决方案8】:

        此函数获取 URL,然后返回图像 BASE64

        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");
        
                alert(dataURL.replace(/^data:image\/(png|jpg);base64,/, ""));
            };
        
            img.src = url;
        }
        

        这样称呼它: getBase64FromImageUrl("images/slbltxt.png")

        【讨论】:

        • 有没有办法将图像从外部链接转换为 base 64?
        • @dinodsaurus 您可以将其加载到图像标签中,或者进行 ajax 查询。
        • 好吧,它要求他们实现 CORS,但是你只需执行 $.ajax(theimageurl) 并且它应该返回一些合理的东西。否则(如果他们没有启用 CORS)它将无法工作;互联网的安全模型不允许这样做。当然,除非你在一个 chrome 插件中,在这种情况下一切都是允许的——即使是上面的例子
        • 你必须把img.src =放在img.onload =之后,因为在某些浏览器中,比如Opera,这个事件不会发生。
        • @Hakkar 内存泄漏只会发生在仍然使用reference count 通知垃圾收集的旧浏览器中。据我了解,循环引用不会在mark and sweep 设置中造成泄漏。一旦退出getBase64FromImageUrl(url)img.onload = function () 的函数范围,img 将无法访问并被垃圾收集。除了 IE 6/7,这没关系。
        猜你喜欢
        • 2015-12-26
        • 2013-06-06
        • 2021-11-21
        • 1970-01-01
        • 2021-12-16
        相关资源
        最近更新 更多