だから私は<img>
自分のページにタグを持っています。
<img id="the_image" src="/imageServlet">
/imageServlet が、ブラウザーがこの画像をキャッシュしてはならないことを示すいくつかの HTTP 応答ヘッダーで応答していると仮定しましょう。この場合の理由は、画像に従業員の機密情報が含まれている可能性があり、特定の国では、マシンが盗まれた場合にブラウザーが (ユーザーの同意なしに) クライアントのハード ドライブに画像を保存してはならないという法律があるためです。応答ヘッダーはCache-Control: no-cache
私が本当にやりたいことは、この画像タグを複製することだけですが、この画像に対して別の HTTP 要求が行われることは望ましくありません。
私のアプリケーションは単一のページで、javascript を使用してこれらの画像タグを動的に使用していますが、この同じ画像を複数の場所に表示したいと考えています。たとえば、画像はユーザーのプロフィール写真のサムネイル バージョンである可能性があります。ある場所ではサムネイルを小さなリストとして表示し、そのユーザーをクリックすると同じ画像をポップアップで表示します。
この src で新しいタグを作成するたび<img>
に、ブラウザはサーバーに新しい呼び出しを行って画像を取得しています。ユーザーがブラウザーで「更新」を押して、その画像の新しい HTTP 要求が読み込まれるかどうかは気にしませんが、1 つのページの読み込み内で、画像データがメモリ内で利用可能であってはならないので、再利用できませんでしたこれ?
どうすればこれを回避できますか? これは、画像が再利用されて複数の場所に表示されるページのパフォーマンスに大きな影響を与え、(ユーザーが他のページに移動したりブラウザーを更新したりしていなくても) 毎回再読み込みする必要があります。
これは、アプリケーションが画像をロードしたいときはいつでも「loadImage」を呼び出し、最終的に自由に使用できる HTML 要素でコールバックを呼び出すという考えで、今書いたコードです。アイデアは、この中央関数がイメージを作成することですがsrc
、HTTP 応答ヘッダーに含まれているかどうかに関係なく、一意の HTTP 要求が 1 つだけ作成されるようにしますCache-Control: no-cache
。
(function(){
var HANDLERS = {};
/**
* I want to keep around a cached "Image" then the "callback" will be called with a
* duplicate somehow - whatever comes back from here can be inserted somewhere, but
* hopefully no additional HTTP requests needs to be made!!
* @param {Image} img
* @return {Image} A copy of the incoming img
*/
function duplicateImage(img) {
// Does not work!! D'oh, is this even possible to achieve??
return img.cloneNode(true);
}
/**
* Users can call this to load the image. The callback is called when the image is ready.
* In case the image fails the callback will be given an object that contains a success flag.
*
* Example:
* loadImage('/imageServlet?xxxx', function(result) {
* if (result.success) { $(body).append(result.image); }
* else { console.log("The image failed to load!") }
* });
*
* @param {string} src The src for the image
* @param {function({success:boolean, image:Image})} callback Give a callback to be called when ready
*/
window.loadImage = function(src, callback) {
if (!HANDLERS[src]) {
var queue = [];
// This loadImage can be called more than once with the same src
// before the image has successfully loaded. We will queue up any
// callbacks and call them later, then replace this with function
// that will directly invoke the callback later.
HANDLERS[src] = function(callback) {
queue.push(callback);
}
// Create the image here, but do it only once!!
var el = new Image();
// When the image loads, we will keep it around in the el
// variable, but the callback will be called with a copy
el.onload = function() {
el.onload = el.onerror = null;
var call = HANDLERS[src] = function(callback) {
callback({
success: true,
image: duplicateImage(el) // This is where the image is duplicated!
});
}
for (var i=0; i<queue.length; i++) {
call(queue[i]);
}
}
// This is just in case the image fails to load. Call any
// queued callbacks with the success false.
el.onerror = function() {
el.onload = el.onerror = null;
var call = HANDLERS[src] = function(callback) {
callback({success: false});
}
for (var i=0; i<queue.length; i++) {
call(queue[i]);
}
}
el.src = src;
}
HANDLERS[src](callback);
}
})();