From 428a67c410a52292c5c599231bff18b38dc4bbaf Mon Sep 17 00:00:00 2001 From: mossroy Date: Sat, 9 Jan 2016 17:49:39 +0100 Subject: [PATCH] Fix big memory overhead due to Base64 encoding of images in src attribute, in jQuery mode Fixes #161. Each image is now converted to a Blob with a URL that is used by the img tag. I applied the same thing for math images (Evopedia archives) --- tests/tests.js | 2 +- www/js/app.js | 9 ++++----- www/js/lib/archive.js | 11 ++++++++--- www/js/lib/util.js | 22 ++++++++++++++++++++++ 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/tests/tests.js b/tests/tests.js index dfcb8b3e..43ad25c7 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -236,7 +236,7 @@ define(['jquery', 'title', 'archive', 'zimArchive', 'zimDirEntry', 'util', 'geom var callbackFunction = function(data) { ok(data && data.length > 0, "Image not empty"); // edb3069b82c68d270f6642c171cc6293.png should give a "1 1/2" formula (can be found in "Rational_number" article) - equal(data, + equal(util.uint8ArrayToBase64(data), "iVBORw0KGgoAAAANSUhEUgAAABUAAAApBAMAAAAogX9zAAAAMFBMVEX///8AAADm5uZAQEDMzMwWFhYiIiIwMDBQUFCenp62trZiYmIMDAwEBASKiop0dHRvDVFEAAAAb0lEQVQY02NggAAmAwY4cE2AM9VNEWwG9oFhcxgKN9HJhYyCQCBApgs5jYMVYCKrGdgOwNgGDCzSMLYwA4MYjH2cgeEawjgWCQSbQwjBdpyAYMch2f4Awd7HwAVj8n1g4Iaxl+7e3Q1jXxQUlGMAAJkfGS29Qu04AAAAAElFTkSuQmCC", "Math image corresponds to '1 1/2' png"); start(); diff --git a/www/js/app.js b/www/js/app.js index fcc7aca8..db99b49c 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -974,7 +974,7 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction if (m) { // It's a math image (Evopedia archive) selectedArchive.loadMathImage(m[1], function(data) { - image.attr("src", 'data:image/png;base64,' + data); + util.feedNodeWithBlob(image, 'src', data, 'image/png'); }); } else { // It's a standard image contained in the ZIM file @@ -982,8 +982,8 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction if (imageMatch) { selectedArchive.getTitleByName(imageMatch[1]).then(function(title) { selectedArchive.readBinaryFile(title, function (readableTitleName, content) { - // TODO : add the complete MIME-type of the image (as read from the ZIM file) - image.attr("src", 'data:image;base64,' + util.uint8ArrayToBase64(content)); + // TODO : use the complete MIME-type of the image (as read from the ZIM file) + util.feedNodeWithBlob(image, 'src', content, 'image'); }); }).fail(function () { console.error("could not find title for image:" + imageMatch[1]); @@ -1001,8 +1001,7 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction var titleName = util.removeUrlParameters(hrefMatch[1]); selectedArchive.getTitleByName(titleName).then(function(title) { selectedArchive.readBinaryFile(title, function (readableTitleName, content) { - var cssContent = encodeURIComponent(util.uintToString(content)); - link.attr("href", 'data:text/css;charset=UTF-8,' + cssContent); + util.feedNodeWithBlob(link, 'href', content, 'text/css'); }); }).fail(function () { console.error("could not find title for CSS : " + hrefMatch[1]); diff --git a/www/js/lib/archive.js b/www/js/lib/archive.js index 3dd103a6..e4670473 100644 --- a/www/js/lib/archive.js +++ b/www/js/lib/archive.js @@ -423,6 +423,11 @@ define(['normalize_string', 'geometry', 'title', 'util', 'titleIterators', 'q'], * @callback callbackStringContent * @param {String} content String content */ + + /** + * @callback callbackUint8ArrayContent + * @param {Uint8Array} content String content + */ /** @@ -545,10 +550,10 @@ define(['normalize_string', 'geometry', 'title', 'util', 'titleIterators', 'q'], /** * Load the math image specified by the hex string and call the - * callbackFunction with a base64 encoding of its data. + * callbackFunction with its Uint8Array data. * * @param {String} hexString - * @param {callbackStringContent} callbackFunction + * @param {callbackUint8ArrayContent} callbackFunction */ LocalArchive.prototype.loadMathImage = function(hexString, callbackFunction) { var entrySize = 16 + 4 + 4; @@ -566,7 +571,7 @@ define(['normalize_string', 'geometry', 'title', 'util', 'titleIterators', 'q'], var blob = mathDataFile.slice(pos, pos + length); reader.onload = function(e) { var byteArray = new Uint8Array(e.target.result); - callbackFunction(util.uint8ArrayToBase64(byteArray)); + callbackFunction(byteArray); }; reader.readAsArrayBuffer(blob); }); diff --git a/www/js/lib/util.js b/www/js/lib/util.js index 7fbfb511..9cf7845e 100644 --- a/www/js/lib/util.js +++ b/www/js/lib/util.js @@ -193,6 +193,27 @@ define(['q'], function(q) { return blob; } + /** + * Creates a Blob from the given content, then a URL from this Blob + * And put this URL in the attribute of the DOM node + * + * This is useful to inject images inside an article + * + * @param {Object} jQueryNode + * @param {String} nodeAttribute + * @param {Uint8Array} content + * @param {String} mimeType + */ + function feedNodeWithBlob(jQueryNode, nodeAttribute, content, mimeType) { + var blob = new Blob([content], {type: mimeType}); + var url = URL.createObjectURL(blob); + jQueryNode.on('load', function () { + URL.revokeObjectURL(url); + }); + jQueryNode.attr(nodeAttribute, url); + } + + /** * Converts a UInt Array to a UTF-8 encoded string * source : http://michael-rushanan.blogspot.de/2014/03/javascript-uint8array-hacks-and-cheat.html @@ -231,6 +252,7 @@ define(['q'], function(q) { readFileSlice : readFileSlice, binarySearch: binarySearch, b64toBlob: b64toBlob, + feedNodeWithBlob: feedNodeWithBlob, uintToString: uintToString, removeUrlParameters: removeUrlParameters };