【问题标题】:which function should I use? .toDataURL() or .toBlob()?我应该使用哪个功能? .toDataURL() 还是 .toBlob()?
【发布时间】:2020-03-20 02:14:43
【问题描述】:

我想使用 JavaScript 在客户端调整图像大小。我找到了两种解决方案,一种是使用.toDataURL() 函数,另一种是使用.toBlob() 函数。两种解决方案都有效。好吧,我只是好奇这两个功能有什么区别?哪一个更好?或者我什么时候应该使用.toDataURL()函数或.toBlob()函数?

这是我用来输出这两个函数的代码,我在图像大小(字节)和图像颜色方面略有不同(我不确定这个)。代码有问题吗?

<html>
    <head>
        <title>Php code compress the image</title>
    </head>
    <body>
        <input id="file" type="file" onchange="fileInfo();"><br>
        <div>
            <h3>Original Image</h3>
            <img id="source_image"/>
            <button onclick="resizeImage()">Resize Image</button>
            <button onclick="compressImage()">Compress Image</button>
            <h1 id="source_image_size"></h1>
        </div>
        <div>
            <h3>Resized Image</h3>
            <h1> image from dataURL function </h1>
            <img id="result_resize_image_dataURL"/>

            <h1> image from toBlob function </h1>
            <img id="result_resize_image_toBlob"/>
        </div>
        <div>
            <fieldset>
                <legend>Console output</legend>
                <div id='console_out'></div>
            </fieldset>
        </div>

        <script>
            //Console logging (html)
            if (!window.console)
            console = {};

            var console_out = document.getElementById('console_out');
            var output_format = "jpg";

            console.log = function(message) {
                console_out.innerHTML += message + '<br />';
                console_out.scrollTop = console_out.scrollHeight;
            }

            var encodeButton = document.getElementById('jpeg_encode_button');
            var encodeQuality = document.getElementById('jpeg_encode_quality');

            function fileInfo(){
                var preview = document.getElementById('source_image');
                var file   = document.querySelector('input[type=file]').files[0];
                var reader  = new FileReader();
                reader.addEventListener("load", function(e) {
                    preview.src = e.target.result;
                }, false);
                if (file) {
                    reader.readAsDataURL(file);
                }
            }
            function resizeImage() {
                var loadedData = document.getElementById('source_image');
                var result_image = document.getElementById('result_resize_image_dataURL');

                var cvs = document.createElement('canvas'),ctx;
                cvs.width = Math.round(loadedData.width/4);
                cvs.height = Math.round(loadedData.height/4);
                var ctx = cvs.getContext("2d").drawImage(loadedData, 0, 0, cvs.width, cvs.height);
                cvs.toBlob(function(blob) {
                    var newImg = document.getElementById('result_resize_image_toBlob'),
                        url = URL.createObjectURL(blob);
                    newImg.onload = function() {
                        // no longer need to read the blob so it's revoked
                        URL.revokeObjectURL(url);
                    };

                    newImg.src = url;
                    console.log(blob.size/1024);
                }, 'image/jpeg', 0.92);

                var newImageData = cvs.toDataURL('image/jpeg', 0.92);
                var result_image_obj = new Image();
                result_image_obj.src = newImageData;
                result_image.src = result_image_obj.src;
                var head = 'data:image/png;base64,';
                var imgFileSize = ((newImageData.length - head.length)*3/4)/1024;
                console.log(imgFileSize);
            }

已编辑:

基于Result of html5 Canvas getImageData or toDataURL - Which takes up more memory?,其表示

"DataURL (BASE64) 是将imageData压缩成JPG或PNG,然后转成字符串,这个字符串比BLOB大37%(信息)。"

字符串是什么意思?它与字节大小相同吗?使用我上面提供的代码,大小差异小于 1Kb(小于 1%)。 .toBlob() 总是优于 .toDataURL() 功能吗?还是有特定条件使用.toDataURL()函数会更好?

【问题讨论】:

  • Stack Overflow 努力让事情尽可能不发表意见,所以没有人(希望)会告诉你哪个“更好”。这两种功能都有各自的用途 - 由您决定,根据您的具体情况,哪一种更合适。
  • @HereticMonkey 我不同意,在这种特殊情况下,有一个明确的“更好”选项。 toDataURL 没有 toBlob 没有的用途。这有点像“我应该使用
    还是 css 来使我的元素居中”之类的问题,除了 toDataURL 没有官方的弃用消息(还没有),因为它不幸被广泛使用......
  • @Kaiido 这不像&lt;center&gt; vs. CSS,因为toDataURLtoBlob 都是在 HTML 5 中定义的,而不是在 HTML 中定义的一个在 CSS 中。但要清楚;我们不应该对哪个“更好”进行投票,而应该做出事实陈述,读者可以从中得出自己的结论。您的回答几乎可以做到这一点。考虑到it's still in HTML 5.3, with no reference to potential deprecation or obsolesence,如果引用诸如“规范中的早期错误”之类的断言会更好
  • 事实上,甚至有人讨论过取代 toBlob 以支持基于 Promise 的 asBlobconvertToBlob 可用离屏画布。但是,不,没有计划从规范中删除它,正如我所说,它的使用实在太广泛了,现在没有办法回来,但也没有理由在新的应用程序中使用它。 Ps:不再遵循 w3c 的定义,没有人能理解 html5.x 之类的东西,你应该阅读 whatwg 代替(toDataURL 也会持续)

标签: javascript canvas html5-canvas


【解决方案1】:

肯定会选择toBlob()
toDataURL 实际上只是规范中的一个早期错误,如果它在几个月后被定义,它肯定不会再出现了,因为我们可以做到相同但使用toBlob 更好。

toDataURL() 是同步的,在执行不同操作时会阻塞你的 UI

  • 将位图从 GPU 移动到 CPU
  • 转换为图片格式
  • 转换为base64字符串

toBlob()另一方面只会同步做第一个子弹,但会以非阻塞的方式转换为图像格式。此外,它根本不会执行第三个项目符号。
所以在原始操作中,这意味着toBlob() 做得更少,以更好的方式。

toDataURL 结果比 toBlob 占用更多内存。

toDataURL返回的data-URL是一个USVString,里面包含了base64压缩的完整二进制数据。
正如您问题中的引述所说,base64 编码本身意味着二进制数据现在将大约 37%。但在这里它不仅编码为 base64,而且使用 UTF-16 编码存储,这意味着每个 ascii 字符将占用原始 ascii 文本所需内存的两倍,我们跳转到一个比原始二进制数据大 174% 的文件。 ..
而且还没有结束...每次您将在某处使用此字符串时,例如作为 DOM 元素* 的 src,或通过网络请求发送它时,此字符串可以再次在内存中重新分配。
*虽然现代浏览器似乎有处理这种情况的机制

(几乎) 永远不需要数据网址。

使用 data-url 可以做的所有事情,使用 Blob 和 Blob-URI 可以做得更好。

要显示或本地链接到二进制数据(例如供用户下载),请使用 Blob-URI,使用 URL.createObjectURL() 方法。
它只是一个指向 Blob 本身指向的内存中保存的二进制数据的指针。这意味着您可以根据需要多次复制 blob-URI,它将始终是约 100 个字符的 UTF-16 字符串,并且二进制数据不会移动。

如果您希望将二进制数据发送到服务器,send it as binary directly,或通过multipart/formdata request

如果您想在本地保存,请使用IndexedDB,它可以保存二进制文件。在 LocalStorage 中存储二进制文件是一个非常糟糕的主意,因为 Storage 对象必须在每次页面加载时加载。

您可能需要 data-urls 的唯一情况是,如果您希望创建需要嵌入二进制数据的独立文档,可以在当前文档死亡后访问。但即便如此,要从画布创建图像的 data-url 版本,请使用 FileReader,将 canvas.toBlob() 返回的 Blob 传递给它。这将实现完整的异步转换,避免您的 UI 无缘无故地被阻止。

【讨论】:

  • 这是一个绝妙的答案。非常感谢您的详细解释。什么情况下 dataURL 是合理的?
  • 相关问题:我们使用 base64 存储操纵图形的历史状态,但这个答案表明使用 blob URL 可以显着提高性能。为了彻底,blob URL 与 base64 图像有什么缺点吗?
  • @Crashalot 我说“你(几乎)永远不需要数据 URL”,而不是 toDataURL,正如我稍后解释的那样,即使在极少数情况下你可能需要数据 URL,toBlob + FileReader .readAsDataURL 会更好。现在,可以证明 toDataURL 的一种情况是您实际上需要同步性。例如,我确实在一些项目中使用它来测试画布是否被污染(例如通过跨域数据)。这个想法是绘制画布以在 1x1px 画布上进行测试,并将该 1px 画布称为 toDataURL。作为单像素画布,缺点不算太大,结果是同步返回的。
  • @Crashalot 我不能保证它会*显着提高性能,但至少应该更容易记忆。 blob:// URL 的缺点是它们会随着创建它的文档而死(您不能存储在 LocalStorage 中,尽管在这种情况下,您可以将 Blob 存储在 IndexedDB 中),而且释放起来有点复杂链接的 Blob 使用的内存。使用 data:// URI,一旦你取消引用字符串,就可以释放内存,使用 blob:// URI,你需要显式调用 URL.revokeObjectURL,如果你失去了 blob:// URI, blob 将一直存在到文档死亡。
  • 感谢您的解释!最后一个问题:OP 暗示了质量差异(例如图像颜色)。是否存在已知差异,如果有,哪种方法更能保留原始图像质量?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-16
  • 2010-11-16
  • 2011-10-17
  • 2010-12-27
  • 1970-01-01
  • 2017-03-03
  • 2014-06-22
相关资源
最近更新 更多