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 @@
function readBlobAsText(blob) {
function readBlobAsText(blob, encoding) {
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.readAsText(blob);
fr.readAsText(blob, encoding);
}, function () {
fr.abort();
});
......@@ -178,6 +178,19 @@
}
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 @@
if (!(param._blob instanceof Blob) &&
typeof param._data === 'string') {
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._mimetype;
......
......@@ -4,8 +4,9 @@
* http://www.gnu.org/licenses/lgpl.html
*/
/*jslint nomen: true */
/*global jIO, sessionStorage, localStorage, Blob, RSVP */
/*jslint nomen: true*/
/*global jIO, sessionStorage, localStorage, Blob, RSVP, atob,
ArrayBuffer, Uint8Array*/
/**
* JIO Local Storage. Type = 'local'.
......@@ -21,7 +22,8 @@
* @class LocalStorage
*/
(function (jIO, sessionStorage, localStorage, Blob, RSVP) {
(function (jIO, sessionStorage, localStorage, Blob, RSVP,
atob, ArrayBuffer, Uint8Array) {
"use strict";
function LocalStorage(spec) {
......@@ -59,6 +61,23 @@
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) {
restrictDocumentId(param._id);
......@@ -70,7 +89,7 @@
404
);
}
return {data: new Blob([textstring])};
return {data: dataURItoBlob(textstring)};
};
LocalStorage.prototype.putAttachment = function (param) {
......@@ -81,7 +100,7 @@
// download data
return new RSVP.Queue()
.push(function () {
return jIO.util.readBlobAsText(param._blob);
return jIO.util.readBlobAsDataURL(param._blob);
})
.push(function (e) {
context._storage.setItem(param._attachment, e.target.result);
......@@ -107,4 +126,5 @@
jIO.addStorage('local', LocalStorage);
}(jIO, sessionStorage, localStorage, Blob, RSVP));
}(jIO, sessionStorage, localStorage, Blob, RSVP,
atob, ArrayBuffer, Uint8Array));
......@@ -1257,7 +1257,7 @@
"_id": "foo",
"_key_path": "foo_attachment",
"info": {
"content_type": "",
"content_type": "text/plain;charset=utf-8",
"length": 3000000
}
}, "put first argument");
......
/*jslint nomen: true */
/*global sessionStorage, localStorage, Blob, document*/
(function (jIO, sessionStorage, localStorage, QUnit, Blob, document) {
/*global sessionStorage, localStorage, Blob, document, btoa,
unescape, HTMLCanvasElement, XMLHttpRequest*/
(function (jIO, sessionStorage, localStorage, QUnit, Blob, document,
btoa, unescape, HTMLCanvasElement, XMLHttpRequest) {
"use strict";
var test = QUnit.test,
stop = QUnit.stop,
......@@ -89,7 +91,7 @@
stop();
expect(1);
localStorage[attachment] = "bar";
localStorage.setItem(attachment, "bar");
this.jio.get({"_id": id})
.then(function (result) {
......@@ -165,12 +167,13 @@
test("get string attachment from document", function () {
var id = "/",
value = "azertyuio\npàç_è-('é&",
value = "azertyuio\npàç_è-('é&こんいちは",
attachment = "stringattachment";
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({
"_id": id,
......@@ -178,8 +181,9 @@
})
.then(function (result) {
ok(result.data instanceof Blob, "Data is Blob");
deepEqual(result.data.type, "", "Check mimetype");
deepEqual(result.data.size, 24, "Check size");
deepEqual(result.data.type, "text/plain;charset=utf-8",
"Check mimetype");
return jIO.util.readBlobAsText(result.data);
})
.then(function (result) {
......@@ -199,7 +203,6 @@
imgCanvas = document.createElement("canvas"),
imgContext = imgCanvas.getContext("2d"),
data_url,
value,
attachment = "stringattachment";
stop();
expect(2);
......@@ -212,25 +215,18 @@
imgContext.fillText("Zibri", 100, 100);
data_url = imgCanvas.toDataURL("image/png");
localStorage.setItem(attachment, data_url);
return jIO.util.ajax({
url: data_url
return context.jio.getAttachment({
"_id": id,
"_attachment": attachment
})
.then(function (result) {
value = result.target.response;
localStorage[attachment] = value;
return context.jio.getAttachment({
"_id": id,
"_attachment": attachment
});
})
.then(function (result) {
ok(result.data instanceof Blob, "Data is Blob");
return jIO.util.readBlobAsText(result.data);
return jIO.util.readBlobAsDataURL(result.data);
})
.then(function (result) {
equal(result.target.result, value, "Attachment correctly fetched");
equal(result.target.result, data_url, "Attachment correctly fetched");
})
.fail(function (error) {
ok(false, error);
......@@ -288,7 +284,11 @@
"_data": value
})
.then(function () {
equal(localStorage[attachment], value);
equal(
localStorage.getItem(attachment),
"data:text/plain;charset=utf-8;base64," +
"YXplcnR5dWlvCnDDoMOnX8OoLSgnw6km"
);
})
.fail(function (error) {
ok(false, error);
......@@ -304,8 +304,6 @@
imgCanvas = document.createElement("canvas"),
imgContext = imgCanvas.getContext("2d"),
data_url,
original_blob,
value,
attachment = "stringattachment";
stop();
expect(1);
......@@ -318,28 +316,41 @@
imgContext.fillText("Zibri", 100, 100);
data_url = imgCanvas.toDataURL("image/png");
return jIO.util.ajax({
url: data_url
})
.then(function (result) {
value = result.target.response;
original_blob = new Blob([result.target.response],
{type: "image/png"});
return context.jio.putAttachment({
"_id": id,
"_attachment": attachment,
"_blob": original_blob
});
})
.then(function () {
equal(localStorage[attachment], value);
})
.fail(function (error) {
ok(false, error);
if (HTMLCanvasElement.prototype.toBlob === undefined) {
Object.defineProperty(
HTMLCanvasElement.prototype,
'toBlob',
{
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({
"_id": id,
"_attachment": attachment,
"_blob": blob
})
.always(function () {
start();
});
.then(function () {
equal(localStorage.getItem(attachment), data_url);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
});
/////////////////////////////////////////////////////////////////
......@@ -378,7 +389,7 @@
var id = "/",
attachment = "foo";
localStorage[attachment] = "bar";
localStorage.setItem(attachment, "bar");
stop();
expect(1);
......@@ -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