Commit c287286c authored by Romain Courteaud's avatar Romain Courteaud

LocalStorage: fix storage of binary data.

Do not store data as encoded string but as data URL.
Set a default mime type for text attachment if not provided.
parent 27a3b950
...@@ -152,13 +152,13 @@ ...@@ -152,13 +152,13 @@
function readBlobAsText(blob) { function readBlobAsText(blob, encoding) {
var fr = new FileReader(); var fr = new FileReader();
return new RSVP.Promise(function (resolve, reject, notify) { return new RSVP.Promise(function (resolve, reject, notify) {
fr.addEventListener("load", resolve); fr.addEventListener("load", resolve);
fr.addEventListener("error", reject); fr.addEventListener("error", reject);
fr.addEventListener("progress", notify); fr.addEventListener("progress", notify);
fr.readAsText(blob); fr.readAsText(blob, encoding);
}, function () { }, function () {
fr.abort(); fr.abort();
}); });
...@@ -178,6 +178,19 @@ ...@@ -178,6 +178,19 @@
} }
util.readBlobAsArrayBuffer = readBlobAsArrayBuffer; util.readBlobAsArrayBuffer = readBlobAsArrayBuffer;
function readBlobAsDataURL(blob) {
var fr = new FileReader();
return new RSVP.Promise(function (resolve, reject, notify) {
fr.addEventListener("load", resolve);
fr.addEventListener("error", reject);
fr.addEventListener("progress", notify);
fr.readAsDataURL(blob);
}, function () {
fr.abort();
});
}
util.readBlobAsDataURL = readBlobAsDataURL;
...@@ -327,7 +340,7 @@ ...@@ -327,7 +340,7 @@
if (!(param._blob instanceof Blob) && if (!(param._blob instanceof Blob) &&
typeof param._data === 'string') { typeof param._data === 'string') {
param._blob = new Blob([param._data], { param._blob = new Blob([param._data], {
"type": param._content_type || param._mimetype || "" "type": param._content_type || param._mimetype || "text/plain;charset=utf-8"
}); });
delete param._data; delete param._data;
delete param._mimetype; delete param._mimetype;
......
...@@ -4,8 +4,9 @@ ...@@ -4,8 +4,9 @@
* http://www.gnu.org/licenses/lgpl.html * http://www.gnu.org/licenses/lgpl.html
*/ */
/*jslint nomen: true */ /*jslint nomen: true*/
/*global jIO, sessionStorage, localStorage, Blob, RSVP */ /*global jIO, sessionStorage, localStorage, Blob, RSVP, atob,
ArrayBuffer, Uint8Array*/
/** /**
* JIO Local Storage. Type = 'local'. * JIO Local Storage. Type = 'local'.
...@@ -21,7 +22,8 @@ ...@@ -21,7 +22,8 @@
* @class LocalStorage * @class LocalStorage
*/ */
(function (jIO, sessionStorage, localStorage, Blob, RSVP) { (function (jIO, sessionStorage, localStorage, Blob, RSVP,
atob, ArrayBuffer, Uint8Array) {
"use strict"; "use strict";
function LocalStorage(spec) { function LocalStorage(spec) {
...@@ -59,6 +61,23 @@ ...@@ -59,6 +61,23 @@
return doc; return doc;
}; };
// https://gist.github.com/davoclavo/4424731
function dataURItoBlob(dataURI) {
// convert base64 to raw binary data held in a string
var byteString = atob(dataURI.split(',')[1]),
// separate out the mime component
mimeString = dataURI.split(',')[0].split(':')[1],
// write the bytes of the string to an ArrayBuffer
arrayBuffer = new ArrayBuffer(byteString.length),
_ia = new Uint8Array(arrayBuffer),
i;
mimeString = mimeString.slice(0, mimeString.length - ";base64".length);
for (i = 0; i < byteString.length; i += 1) {
_ia[i] = byteString.charCodeAt(i);
}
return new Blob([arrayBuffer], {type: mimeString});
}
LocalStorage.prototype.getAttachment = function (param) { LocalStorage.prototype.getAttachment = function (param) {
restrictDocumentId(param._id); restrictDocumentId(param._id);
...@@ -70,7 +89,7 @@ ...@@ -70,7 +89,7 @@
404 404
); );
} }
return {data: new Blob([textstring])}; return {data: dataURItoBlob(textstring)};
}; };
LocalStorage.prototype.putAttachment = function (param) { LocalStorage.prototype.putAttachment = function (param) {
...@@ -81,7 +100,7 @@ ...@@ -81,7 +100,7 @@
// download data // download data
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return jIO.util.readBlobAsText(param._blob); return jIO.util.readBlobAsDataURL(param._blob);
}) })
.push(function (e) { .push(function (e) {
context._storage.setItem(param._attachment, e.target.result); context._storage.setItem(param._attachment, e.target.result);
...@@ -107,4 +126,5 @@ ...@@ -107,4 +126,5 @@
jIO.addStorage('local', LocalStorage); jIO.addStorage('local', LocalStorage);
}(jIO, sessionStorage, localStorage, Blob, RSVP)); }(jIO, sessionStorage, localStorage, Blob, RSVP,
atob, ArrayBuffer, Uint8Array));
...@@ -1257,7 +1257,7 @@ ...@@ -1257,7 +1257,7 @@
"_id": "foo", "_id": "foo",
"_key_path": "foo_attachment", "_key_path": "foo_attachment",
"info": { "info": {
"content_type": "", "content_type": "text/plain;charset=utf-8",
"length": 3000000 "length": 3000000
} }
}, "put first argument"); }, "put first argument");
......
/*jslint nomen: true */ /*jslint nomen: true */
/*global sessionStorage, localStorage, Blob, document*/ /*global sessionStorage, localStorage, Blob, document, btoa,
(function (jIO, sessionStorage, localStorage, QUnit, Blob, document) { unescape, HTMLCanvasElement, XMLHttpRequest*/
(function (jIO, sessionStorage, localStorage, QUnit, Blob, document,
btoa, unescape, HTMLCanvasElement, XMLHttpRequest) {
"use strict"; "use strict";
var test = QUnit.test, var test = QUnit.test,
stop = QUnit.stop, stop = QUnit.stop,
...@@ -89,7 +91,7 @@ ...@@ -89,7 +91,7 @@
stop(); stop();
expect(1); expect(1);
localStorage[attachment] = "bar"; localStorage.setItem(attachment, "bar");
this.jio.get({"_id": id}) this.jio.get({"_id": id})
.then(function (result) { .then(function (result) {
...@@ -165,12 +167,13 @@ ...@@ -165,12 +167,13 @@
test("get string attachment from document", function () { test("get string attachment from document", function () {
var id = "/", var id = "/",
value = "azertyuio\npàç_è-('é&", value = "azertyuio\npàç_è-('é&こんいちは",
attachment = "stringattachment"; attachment = "stringattachment";
stop(); stop();
expect(4); expect(3);
localStorage[attachment] = value; localStorage.setItem(attachment, "data:text/plain;charset=utf-8;base64," +
btoa(unescape(encodeURIComponent(value))));
this.jio.getAttachment({ this.jio.getAttachment({
"_id": id, "_id": id,
...@@ -178,8 +181,9 @@ ...@@ -178,8 +181,9 @@
}) })
.then(function (result) { .then(function (result) {
ok(result.data instanceof Blob, "Data is Blob"); ok(result.data instanceof Blob, "Data is Blob");
deepEqual(result.data.type, "", "Check mimetype"); deepEqual(result.data.type, "text/plain;charset=utf-8",
deepEqual(result.data.size, 24, "Check size"); "Check mimetype");
return jIO.util.readBlobAsText(result.data); return jIO.util.readBlobAsText(result.data);
}) })
.then(function (result) { .then(function (result) {
...@@ -199,7 +203,6 @@ ...@@ -199,7 +203,6 @@
imgCanvas = document.createElement("canvas"), imgCanvas = document.createElement("canvas"),
imgContext = imgCanvas.getContext("2d"), imgContext = imgCanvas.getContext("2d"),
data_url, data_url,
value,
attachment = "stringattachment"; attachment = "stringattachment";
stop(); stop();
expect(2); expect(2);
...@@ -212,25 +215,18 @@ ...@@ -212,25 +215,18 @@
imgContext.fillText("Zibri", 100, 100); imgContext.fillText("Zibri", 100, 100);
data_url = imgCanvas.toDataURL("image/png"); data_url = imgCanvas.toDataURL("image/png");
localStorage.setItem(attachment, data_url);
return jIO.util.ajax({
url: data_url
})
.then(function (result) {
value = result.target.response;
localStorage[attachment] = value;
return context.jio.getAttachment({ return context.jio.getAttachment({
"_id": id, "_id": id,
"_attachment": attachment "_attachment": attachment
});
}) })
.then(function (result) { .then(function (result) {
ok(result.data instanceof Blob, "Data is Blob"); ok(result.data instanceof Blob, "Data is Blob");
return jIO.util.readBlobAsText(result.data); return jIO.util.readBlobAsDataURL(result.data);
}) })
.then(function (result) { .then(function (result) {
equal(result.target.result, value, "Attachment correctly fetched"); equal(result.target.result, data_url, "Attachment correctly fetched");
}) })
.fail(function (error) { .fail(function (error) {
ok(false, error); ok(false, error);
...@@ -288,7 +284,11 @@ ...@@ -288,7 +284,11 @@
"_data": value "_data": value
}) })
.then(function () { .then(function () {
equal(localStorage[attachment], value); equal(
localStorage.getItem(attachment),
"data:text/plain;charset=utf-8;base64," +
"YXplcnR5dWlvCnDDoMOnX8OoLSgnw6km"
);
}) })
.fail(function (error) { .fail(function (error) {
ok(false, error); ok(false, error);
...@@ -304,8 +304,6 @@ ...@@ -304,8 +304,6 @@
imgCanvas = document.createElement("canvas"), imgCanvas = document.createElement("canvas"),
imgContext = imgCanvas.getContext("2d"), imgContext = imgCanvas.getContext("2d"),
data_url, data_url,
original_blob,
value,
attachment = "stringattachment"; attachment = "stringattachment";
stop(); stop();
expect(1); expect(1);
...@@ -318,21 +316,33 @@ ...@@ -318,21 +316,33 @@
imgContext.fillText("Zibri", 100, 100); imgContext.fillText("Zibri", 100, 100);
data_url = imgCanvas.toDataURL("image/png"); data_url = imgCanvas.toDataURL("image/png");
return jIO.util.ajax({
url: data_url if (HTMLCanvasElement.prototype.toBlob === undefined) {
}) Object.defineProperty(
.then(function (result) { HTMLCanvasElement.prototype,
value = result.target.response; 'toBlob',
original_blob = new Blob([result.target.response], {
{type: "image/png"}); value: function (callback, type, quality) {
var xhr = new XMLHttpRequest();
xhr.open('GET', this.toDataURL(type, quality));
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
callback(new Blob([this.response], {type: type || 'image/png'}));
};
xhr.send();
}
}
);
}
imgCanvas.toBlob(function (blob) {
return context.jio.putAttachment({ return context.jio.putAttachment({
"_id": id, "_id": id,
"_attachment": attachment, "_attachment": attachment,
"_blob": original_blob "_blob": blob
});
}) })
.then(function () { .then(function () {
equal(localStorage[attachment], value); equal(localStorage.getItem(attachment), data_url);
}) })
.fail(function (error) { .fail(function (error) {
ok(false, error); ok(false, error);
...@@ -341,6 +351,7 @@ ...@@ -341,6 +351,7 @@
start(); start();
}); });
}); });
});
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// localStorage.removeAttachment // localStorage.removeAttachment
...@@ -378,7 +389,7 @@ ...@@ -378,7 +389,7 @@
var id = "/", var id = "/",
attachment = "foo"; attachment = "foo";
localStorage[attachment] = "bar"; localStorage.setItem(attachment, "bar");
stop(); stop();
expect(1); expect(1);
...@@ -452,4 +463,5 @@ ...@@ -452,4 +463,5 @@
}); });
}); });
}(jIO, sessionStorage, localStorage, QUnit, Blob, document)); }(jIO, sessionStorage, localStorage, QUnit, Blob, document,
btoa, unescape, HTMLCanvasElement, XMLHttpRequest));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment