【问题标题】:context.drawImage for multiple images eventually causes FireFox to Crash多个图像的 context.drawImage 最终导致 FireFox 崩溃
【发布时间】:2016-01-08 07:09:32
【问题描述】:

我正在创建一个应该能够接受至少 1000 张图片的图片上传器。用户选择/拖放图像后,应显示图像以进行预览。然后,用户将点击上传按钮开始上传。

我已经实现了整个功能,它可以在 Chrome 上运行,就像一个魅力。但这在 Firefox 中失败了。

我创建了一种排队机制,一旦用户选择图像,在某个时间点只有 n 个图像会加载到浏览器中。

超过 20 张图像也会发生崩溃(即使使用排队机制创建一批 1 张图像)。我在 Firefox 中使用 Tab Memory Usage Plugin(http://mybrowseraddon.com/tab-memory.html) 检查了内存使用情况。它甚至会因 40 MB 的内存利用率而崩溃。所以我想,这不是因为任何内存泄漏。

function readAndDisplay(file, index){
    id = getFileId(file);
    reader = new FileReader();

    reader.onload = function (e) {
        addImageCard(file, index);
        resizeImage(e, index);
        delete this;
        //$('#image' + id).attr('src', e.target.result);

    }

    reader.readAsDataURL(file);
}

function resizeImage(fileReaderEvent, index){

    var img = new Image();
    img.onload=function(){
        var MAX_WIDTH = 800;
        var MAX_HEIGHT = 600;
        var width = img.width;
        var height = img.height;

        if (width > height) {
          if (width > MAX_WIDTH) {
            height *= MAX_WIDTH / width;
            width = MAX_WIDTH;
          }
        } else {
          if (height > MAX_HEIGHT) {
            width *= MAX_HEIGHT / height;
            height = MAX_HEIGHT;
          }
        }
        var canvas = document.getElementById("image" + index);
        var ctx = canvas.getContext("2d");
        canvas.width = width;
        canvas.height = height;
        ctx.drawImage(img, 0, 0, width, height);

        img.src = "";
        delete canvas;
        delete ctx;
        img = null;
        delete fileReaderEvent;

        uploadQueue.push(index);
        if(loadImageIterator < updatedFiles.length){
            loadImage(updatedFiles[loadImageIterator++]);
        }
        else{
            updatedFiles = [];
        }

    }
    img.src = fileReaderEvent.target.result;
    var md5 = CryptoJS.MD5(fileReaderEvent.target.result).toString();
    console.log(md5);
}

function addImageCard(file, index){

    imageHolder = $("#imageholder");

    imageCard = $("<div>", {
        class: "col m1 s4",
        id: "card" + index
    });

    innerDiv = $("<div>", {
        class: "card"
    });

    cardImage = $("<div>", {
        class: "card-image",
    }).append($("<canvas>", {
        id: "image" + index,
        css: {
            width: "100%"
        }
        //src: "images/sample-1.jpg"
    })).append($("<span>", {
        class: "card-title",
        text: ""
    }));

    cardContent = $("<div>",{
        class: "card-content",
        html: "<p>" + file.name + "</p>"
    });

    cardAction = $("<div>", {
        class: "card-action"
    });

    removeButton = $("<a>", {
        title: "Remove",
        css: {
            "font-size": "20px"
        },
        click: function(){
            remove(index)
        }

    }).append($("<span>", {
        class: "glyphicon glyphicon-remove-circle"
    }));

    uploadButton = $("<a>", {
        title: "Upload Now",
        css: {
            "font-size": "20px",
            float: "right",
            "margin-right": "4px"
        }

    }).append($("<span>", {
        class: "glyphicon glyphicon-cloud-upload"
    }));

    cardAction.append(removeButton);
    cardAction.append(uploadButton);

    innerDiv.append(cardImage);
    innerDiv.append(cardContent);
    innerDiv.append(cardAction);
    imageCard.append(innerDiv);
    imageHolder.append(imageCard);

}

当我评论以下代码时,Firefox 不会崩溃 ctx.drawImage(img, 0, 0, width, height)

所以到目前为止,我认为问题出在这个 drawImage 上。可能在 Chrome 和 Firefox 中 drawImage 的实现是不同的,我使用它的方式使它在 Chrome 中工作,但在 Firefox 中,它会引发错误。

有人对为什么会发生这个问题有任何具体的想法吗?

JSFiddle-http://jsfiddle.net/sanchit235/7xh1gbj6/

Firefox 崩溃报告-https://crash-stats.mozilla.com/report/index/6107c530-12a1-4aea-96c9-0366c2151010

【问题讨论】:

  • delete 不会像您想的那样做。它只删除对象的属性,不删除对象引用(因此垃圾收集器不会回收对象的内存)。
  • 删除在这里几乎没有意义。我添加它只是为了确定。这里的所有变量都是本地的。因此,理想情况下,它们将被自己收集垃圾。如果其中一些由于我使用它们的方式一定会导致泄漏,请告诉我。
  • 当你不做这些无用的清洁时,是否也会发生这种情况?
  • 即使我正在做这种无用的清洁,它也会发生;) 顺便说一句,如果没有这种无用的清洁,chrome 就会给 Aww Snap。所以我不得不这样做。
  • 对我来说,这似乎不是解决您的“aw snap”问题的正确解决方案。如果我认为它的发生是因为您的代码使页面无响应是正确的,那么您可以尝试requestAnimationFrame 限制。我现在甚至没有时间重现错误,所以我无法判断它是否会起作用,但它应该给浏览器时间来处理每个对象和垃圾收集。

标签: javascript jquery html firefox canvas


【解决方案1】:

您的代码中存在一些问题:

  • 第一个是您在 FileSelectHandler() (onchange) 的 for 循环中同步调用 loadImage(),如果循环未完成,则它本身在绘制完成后异步回调 loadImage()(on我的比较出现了 20 张图像 15 次)。
    只调用一次 onchange 并让异步回调就足够了。

  • 第二个正在检查回调:
    if(loadImageIterator < updatedFiles.length){ loadImage(updatedFiles[loadImageIterator++]); }
    应替换为
    if(++loadImageIterator < updatedFiles.length){ loadImage(updatedFiles[loadImageIterator]); }
    否则第一次调用将通过updatedFiles[0]

  • 第三个是你的变量声明。您忘记了var 之前的readernewFile 和其他一些变量,从而创建了这些全局变量。

这是corrected fiddle,它不会在 Chrome 或 FF 中引发任何错误,也不会自行删除任何内容。

【讨论】:

  • 您好 Kaiido,我的 FF 因您的小提琴而崩溃,但 Chrome 工作正常。你能检查一下你的 FF 是否也会因为我添加的小提琴而崩溃,并且使用与你的小提琴相同的图像集。
  • @SanchitMehta 不,即使是 40*5MB 图片,我也无法从您提供的小提琴中重现您的 FF 崩溃。崩溃前您是否在控制台中收到消息?您是否尝试过一张一张地加载每张图片以查看其中一个文件是否可能是原因?你的 FF 更新了吗?
  • 我确实尝试一次加载 1 张图片。尝试使用 300*5 MB 图像。我相信你会看到错误。同样在 IE11 中,脚本会在一段时间后停止执行(可能浏览器会检测到这一点并停止可能最终导致 IE 崩溃的脚本)。另外,现在我已经删除了无用的删除语句并在每次初始化之前添加了 var (最初我也是这样做的),没有内存泄漏。我想知道,为什么它早在那儿。试试这个-dropzonejs.com他们也在做我正在做的事情,对他们来说也是如此 FF 崩溃:D
  • @SanchitMehta 刚刚打了 900 通电话,没有崩溃,不过粉丝转得更快了。但是我在一个 16GB 内存的 mac 上,也许你的 conf 有问题,你能在另一台设备上重现吗?此外,您的代码已修复,您的崩溃现在来自其他原因。如果您愿意,请向 bugzilla 填写错误。
猜你喜欢
  • 2012-06-28
  • 1970-01-01
  • 2011-07-05
  • 2017-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多