28

サーバーから JSON を高速でストリーミングし、その一部を毎秒約 10 回表示する Web ページがあります。1 つの部分は、base64 でエンコードされた PNG 画像です。画像を表示するいくつかの異なる方法を見つけましたが、それらはすべて無制限のメモリ使用量を引き起こします。数分で 50MB から 2GB に増加します。Chrome、Safari、および Firefox で発生します。IEは試していません。

Activity Monitor.app を見て、最初にメモリ使用量を発見しました。Google Chrome レンダラー プロセスは継続的にメモリを消費します。次に、Chrome のリソース インスペクター ( View> Developer> Developer ToolsResources) を見たところ、画像をキャッシュしていることがわかりました。img srcを変更するか、新しい Image() を作成してその を設定するたびにsrc、Chrome はそれをキャッシュしました。他のブラウザも同じことをしているとしか思えません。

このキャッシュを制御する方法はありますか? オフにすることはできますか?

編集: Safari/Mobile Safari でこの手法を使用できるようにしたいと思います。また、アイデアがあれば、画像をすばやく更新する他の方法も受け入れます。

ここに私が試した方法があります。それぞれが、AJAX の完了時に呼び出される関数に存在します。

方法 1 -タグにsrc属性を直接設定するimg

速い。きれいに表示します。狂ったように漏れます。

$('#placeholder_img').attr('src', 'data:image/png;base64,' + imgString);

方法 2 - に置き換えimgcanvas使用するdrawImage

正常に表示されますが、まだ漏れます。

var canvas = document.getElementById("placeholder_canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function() {
    ctx.drawImage(img, 0, 0); 
}   
img.src = "data:image/png;base64," + imgString;

方法 3 - バイナリに変換してcanvas内容を置き換える

ここで何か間違ったことをしています。画像が小さく表示され、ランダム ノイズのように見えます。この方法では、制御された量のメモリが使用されます (100 MB に増加すると停止します) が、特に Safari では低速です (CPU 使用率は 50% まで、Chrome では 17%)。アイデアは、この同様の SO の質問から生まれました: Safari でのデータ URI リーク (以前は: HTML5 キャンバスでのメモリ リーク)

var img = atob(imgString);
var binimg = [];
for(var i = 0; i < img.length; i++) {
    binimg.push(img.charCodeAt(i));
}
var bytearray = new Uint8Array(binimg);

// Grab the existing image from canvas
var ctx = document.getElementById("placeholder_canvas").getContext("2d");
var width = ctx.canvas.width, 
    height = ctx.canvas.height;
var imgdata = ctx.getImageData(0, 0, width, height);

// Overwrite it with new data
for(var i = 8, len = imgdata.data.length; i < len; i++) {
    imgdata.data[i-8] = bytearray[i];
}

// Write it back
ctx.putImageData(imgdata, 0, 0);
4

8 に答える 8

5

この問題が投稿されてから何年も経っていることは承知していますが、最近のバージョンの Safari ブラウザにはまだ問題が存在します。だから私はすべてのブラウザで動作する決定的な解決策を持っています.これは仕事や命を救うことができると思います!.

次のコードを HTML ページのどこかにコピーします。

// Methods to address the memory leaks problems in Safari
var BASE64_MARKER = ';base64,';
var temporaryImage;
var objectURL = window.URL || window.webkitURL;

function convertDataURIToBlob(dataURI) {
    // Validate input data
    if(!dataURI) return;

    // Convert image (in base64) to binary data
    var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
    var base64 = dataURI.substring(base64Index);
    var raw = window.atob(base64);
    var rawLength = raw.length;
    var array = new Uint8Array(new ArrayBuffer(rawLength));

    for(i = 0; i < rawLength; i++) {
        array[i] = raw.charCodeAt(i);
    }

    // Create and return a new blob object using binary data
    return new Blob([array], {type: "image/jpeg"});
}

base64Image次に、base64 形式 (例: )の新しいフレーム/画像を受け取り、 htmlオブジェクトdata:image/jpeg;base64, LzlqLzRBQ...を更新する場合は、次のコードを使用します。<img />imageElement

// Destroy old image
if(temporaryImage) objectURL.revokeObjectURL(temporaryImage);

// Create a new image from binary data
var imageDataBlob = convertDataURIToBlob(base64Image);

// Create a new object URL object
temporaryImage = objectURL.createObjectURL(imageDataBlob);

// Set the new image
imageElement.src = temporaryImage;

この最後のコードを必要なだけ繰り返しても、メモリ リークは発生しません。このソリューションでは canvas 要素を使用する必要はありませんが、コードを調整して機能させることができます。

于 2016-07-27T23:14:13.340 に答える
3

描画後に image.src = "" を設定してみてください。

var canvas = document.getElementById("placeholder_canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function() {
    ctx.drawImage(img, 0, 0); 
    //after drawing set src empty
    img.src = "";
}   
img.src = "data:image/png;base64," + imgString;

これは助かるかも

于 2015-02-20T08:32:02.387 に答える
3

データ URL のメモリ使用量に関する保証はないと思います。それらを 1 つのブラウザーで動作させる方法を見つけられたとしても、他のブラウザーやバージョンについては、何も保証されないか、ほとんど保証されません。

画像データを BLOB に入れてから BLOB URL を作成すると、そのデータの割り当てを解除できます。

データ URI を BLOB URL に変換する例を次に示します。Chrome 以外のブラウザーや、将来のバージョンの Chrome では、 webkit-&プレフィックスを変更または削除する必要がある場合があります。WebKit-

var parts = dataURL.match(/data:([^;]*)(;base64)?,([0-9A-Za-z+/]+)/);

//assume base64 encoding
var binStr = atob(parts[3]);

//might be able to replace the following lines with just
// var view = new Uint8Array(binStr);
//haven't tested.

//convert to binary in ArrayBuffer
var buf = new ArrayBuffer(binStr.length);
var view = new Uint8Array(buf);
for(var i = 0; i < view.length; i++)
  view[i] = binStr.charCodeAt(i);
//end of the possibly unnecessary lines

var builder = new WebKitBlobBuilder();
builder.append(buf);

//create blob with mime type, create URL for it
var URL = webkitURL.createObjectURL(builder.getBlob(parts[1]))
return URL;

割り当て解除は次のように簡単です。

webkitURL.revokeObjectURL(URL);

また、blob URLをとして使用できますimgsrc

残念ながら、blob URL は v10 より前の IE ではサポートされていないようです。

API リファレンス:

http://www.w3.org/TR/FileAPI/#dfn-createObjectURL

http://www.w3.org/TR/FileAPI/#dfn-revokeObjectURL

互換性参照:

http://caniuse.com/#search=blob%20url

于 2012-03-28T18:56:05.323 に答える
2

私は非常によく似た問題を抱えていました。

img.src を dataUrl に設定するとメモリ リークが発生する

簡単に言えば、私は単純に Image 要素を回避しました。JavaScript デコーダーを使用して、画像データをデコードしてキャンバスに表示します。ユーザーが画像をダウンロードしようとしない限り、違いもわかりません。もう 1 つの欠点は、最新のブラウザーに限定されることです。利点は、このメソッドがふるいのように漏れないことです:)

于 2013-10-14T15:08:02.973 に答える
1

この問題を解決するためにさまざまな方法を使用しましたが、どれも機能しません。img.src = base64string の場合にメモリ リークが発生し、それらのメモリが決して解放されないようです。これが私の解決策です。

fs.writeFile('img0.jpg', img_data, function (err) {
    // console.log("save img!" );
});
document.getElementById("my-img").src =  'img0.jpg?'+img_step;
img_step+=1;

base64 を jpeg バッファに変換する必要があることに注意してください。

My Electron app updating img every 50ms, and memory doesn't leak. Forget about disk usage. Chrome's memory management piss me off.

于 2016-09-07T07:51:12.967 に答える
0

Safari または Mobile Safari がデータの URL を漏らさない限りサーバー側がすべてのブラウザーでこれを行う唯一の方法である可能性があります。

おそらく最も簡単なのは、画像ストリームの URL を作成することです。GETこれにより、302 または 303 応答が返され、目的の画像を提供する使い捨ての URL にリダイレクトされます。URL のリロードを強制するには、おそらくイメージ タグを破棄して再作成する必要があります。

imgまた、キャッシュ動作に関してはブラウザに翻弄されます。そして、HTTP 仕様についての私の理解 (または理解の欠如) の慈悲。それでも、サーバー側の操作が要件に合わない場合を除き、まずこれを試してください。サーバーの複雑さが増しますが、このアプローチではブラウザをより自然に使用できます。

しかし、ブラウザーを不自然に使用する場合はどうでしょうか? ブラウザーが を実装iframeし、関連するコンテンツを処理する方法によっては、メモリをリークすることなくデータ URL を動作させることができる場合があります。これはちょっとフランケンシュタインのたわごとであり、誰もする必要のないナンセンスです。利点:うまくいく可能性があります。欠点: それを試す方法は無数にあり、むらのある、文書化されていない動作はまさに私が期待するものです。

1 つのアイデア:iframeページを含むページを埋め込みます。このページとそれが埋め込まれているページは、クロス ドキュメント メッセージを使用します(互換性マトリックスの緑色に注意してください!)。embeddee は PNG 文字列を取得し、それを埋め込みページに渡します。埋め込みページは適切なimgタグを作成します。埋め込み先が新しいメッセージを表示する必要がある場合、埋め込まれたメッセージを破棄しiframe(できればデータ URL のメモリを解放します)、新しいメッセージを作成し、新しい PNG 文字列を渡します。

もう少し賢くしたい場合は、埋め込みフレームのソースをデータ URL として実際に埋め込み先ページに埋め込むことができます。ただし、これによりそのデータの URL が漏洩する可能性があります。

「Safariで動作するものの方が良いでしょう。」ブラウザ テクノロジは、不均一に進歩し続けています。彼らがプレート上の機能をあなたに渡さないとき、あなたはよこしまになる必要があります。

于 2012-03-29T14:47:19.023 に答える