本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh)
本文作者: 苏洋
创建时间: 2014年06月22日
统计字数: 7292字
阅读时间: 15分钟阅读
本文链接: https://soulteary.com/2014/06/22/localstorage-limit-test.html
-----
# localStorage 容量测试脚本
说到localStorage,尤其是谈到它到底可以存储多少内容的时候,多数人都会沉默。
有的时候你翻浏览器官方release log要么是没有,如果有,也可能在某个小版本突然变动,现在,外壳浏览器越来越多,这件事情就变的更加棘手了。
浏览器的版本更迭很快,不同的终端,不同的系统,不同的浏览器对于这个问题都可能产生不同的结果。
接下来我会完善一个收集脚本(如果哪位愿意写一个,这里就thank you very much鸟),统计大家的测试数据,最后公开数据,造福大家。
前端测试脚本已经写好:
- GitHub地址:[https://github.com/soulteary/localStorage-limit-test](https://github.com/soulteary/localStorage-limit-test)
- 在线demo(记得打开console,我略懒,不想写展示逻辑了):[http://soulteary.github.io/localStorage-limit-test/#!/zh-CN](http://soulteary.github.io/localStorage-limit-test/#!/zh-CN)
同样的,代码如果去掉注释和空行,其实也没多少。
```js
(function (cfg) {
"use strict";
/**
* @file: localStorage 测试脚本
*
* @author: [soulteary](soulteary@qq.com)
* @date: 2014.06.20
* @ver: 0.0.1
* @useage:
*
* --global config(all optional):
* {
* "report": "http://www.localstorage.com/build/collect.php", //默认反馈的服务器接口地址
* "admin": "soulteary", //用于展示邮箱用户名
* "email": "@qq.com", //用于展示服务器邮箱
* "api": { //调用页面全局的api
* "user":"pageReport", //自定义的数据展示处理函数
* "server":"serverReport" //自定义的服务端处理函数
* },
* "silence": true //是否静默处理错误
* }
*
* 如果使用`api`,可以在引用脚本的页面声明全局脚本来处理测试返回的数据。
*
*
*
* 如果使用`silence`,那么即使不能连接服务器,也不会提示访问者。
*
*
* --main page config(high priority):
*
*
*
*/
/**
* 保存传递的配置
*/
var config = cfg;
/**
* 如果页面中存在变量`$LST$`
* 则将其配置覆盖默认配置
*/
if (window.$LST$) {
cfg = window.$LST$;
if (cfg.desc === "LocalStorage Test Config") {
for (var _c in cfg) {
config[_c] = cfg[_c];
}
}
}
/**
* 查看浏览器是否支持localStorage
* @returns {boolean}
*/
function supportFeatures() {
try {
localStorage.setItem("soulteary_check", true);
localStorage.removeItem("soulteary_check");
return true;
} catch (e) {
return false;
}
}
/**
* 记录浏览器写入数据,实际写入数据,以及错误信息
* @returns {{process_write: number, browser_write: number, error: object}}
*/
function getQuota() {
try {
//预先清理环境
localStorage.clear();
// 1TB, 显然不可能,因为SQLLite最大支持2GB.
for (var p = 0, q = 1024; p < q; p++) {
//1GB, 如果浏览器忠实于实现SQLLite的话
for (var i = 0, j = 1024; i < j; i++) {
//1MB, 填充测试数据
for (var m = 0, n = 1024; m < n; m++) {
//1KB, 填充测试数据, JS char每个占用4字节
localStorage.setItem(i + ":" + m, (new Array(256)).join('c'));
}
}
}
} catch (e) {
//清理测试带来的环境污染
localStorage.clear();
return {
"process_write": p * 1024 * 1024 + i * 1024 + m,
"browser_write": localStorage.length,
"error": e
}
}
}
/**
* 通过UA获取浏览器和操作系统信息
* @ref ua@kissy
* @returns {object}
*/
function getUA() {
return (function (e) {
function g(a) {
var b = 0;
return parseFloat(a.replace(/\./g,
function () {
return 0 === b++ ? "." : ""
}))
}
function l(a, b) {
var c;
b.trident = 0.1;
if ((c = a.match(/Trident\/([\d.]*)/)) && c[1]) b.trident = g(c[1]);
b.core = "trident"
}
function k(a) {
var b, c;
return (b = a.match(/MSIE ([^;]*)|Trident.*; rv(?:\s|:)?([0-9.]+)/)) && (c = b[1] || b[2]) ? g(c) : 0
}
function d(a) {
var b, c = "",
d = "",
i, h = [6, 9],
f,
p = n && n.createElement("div"),
D = [],
s = {
webkit: e,
trident: e,
gecko: e,
presto: e,
chrome: e,
safari: e,
firefox: e,
ie: e,
opera: e,
mobile: e,
core: e,
shell: e,
phantomjs: e,
os: e,
ipad: e,
iphone: e,
ipod: e,
ios: e,
android: e,
nodejs: e
};
p && p.getElementsByTagName && (p.innerHTML = "<\!--[if IE {{version}}]>".replace("{{version}}", ""), D = p.getElementsByTagName("s"));
if (0 < D.length) {
l(a, s);
i = h[0];
for (h = h[1]; i <= h; i++) if (p.innerHTML = "<\!--[if IE {{version}}]>".replace("{{version}}", i), 0 < D.length) {
s[d = "ie"] = i;
break
}
if (!s.ie && (f = k(a))) s[d = "ie"] = f
} else if ((i = a.match(/AppleWebKit\/([\d.]*)/)) && i[1]) {
s[c = "webkit"] = g(i[1]);
if ((i = a.match(/OPR\/(\d+\.\d+)/)) && i[1]) s[d = "opera"] = g(i[1]);
else if ((i = a.match(/Chrome\/([\d.]*)/)) && i[1]) s[d = "chrome"] = g(i[1]);
else if ((i = a.match(/\/([\d.]*) Safari/)) && i[1]) s[d = "safari"] = g(i[1]);
if (/ Mobile\//.test(a) && a.match(/iPad|iPod|iPhone/)) {
s.mobile = "apple";
if ((i = a.match(/OS ([^\s]*)/)) && i[1]) s.ios = g(i[1].replace("_", "."));
b = "ios";
if ((i = a.match(/iPad|iPod|iPhone/)) && i[0]) s[i[0].toLowerCase()] = s.ios
} else if (/ Android/i.test(a)) {
if (/Mobile/.test(a) && (b = s.mobile = "android"), (i = a.match(/Android ([^\s]*);/)) && i[1]) s.android = g(i[1])
} else if (i = a.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/)) s.mobile = i[0].toLowerCase();
if ((i = a.match(/PhantomJS\/([^\s]*)/)) && i[1]) s.phantomjs = g(i[1])
} else if ((i = a.match(/Presto\/([\d.]*)/)) && i[1]) {
if (s[c = "presto"] = g(i[1]), (i = a.match(/Opera\/([\d.]*)/)) && i[1]) {
s[d = "opera"] = g(i[1]);
if ((i = a.match(/Opera\/.* Version\/([\d.]*)/)) && i[1]) s[d] = g(i[1]);
if ((i = a.match(/Opera Mini[^;]*/)) && i) s.mobile = i[0].toLowerCase();
else if ((i = a.match(/Opera Mobi[^;]*/)) && i) s.mobile = i[0]
}
} else if (f = k(a)) s[d = "ie"] = f,
l(a, s);
else if (i = a.match(/Gecko/)) {
s[c = "gecko"] = 0.1;
if ((i = a.match(/rv:([\d.]*)/)) && i[1]) s[c] = g(i[1]),
/Mobile|Tablet/.test(a) && (s.mobile = "firefox");
if ((i = a.match(/Firefox\/([\d.]*)/)) && i[1]) s[d = "firefox"] = g(i[1])
}
b || (/windows|win32/i.test(a) ? b = "windows" : /macintosh|mac_powerpc/i.test(a) ? b = "macintosh" : /linux/i.test(a) ? b = "linux" : /rhino/i.test(a) && (b = "rhino"));
s.os = b;
s.core = s.core || c;
s.shell = d;
return s
}
var f = window,
n = f.document,
f = f.navigator,
b = d(f && f.userAgent || "");
if ("object" === typeof process) {
var c, p;
if ((c = process.versions) && (p = c.node)) b.os = process.platform,
b.nodejs = g(p)
}
for (var c in b) {
if (!b[c]) delete b[c];
}
return b;
}());
}
/**
* 创建随机字符串
* @param strLen
* @param opt
* @param cut
* @returns {string}
*/
function randomChars(strLen, opt, cut) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
if (opt) {
switch (opt) {
case 'NUMBER':
chars = "1234567890";
break;
case 'UCASE':
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
break;
case 'LCASE':
chars = "abcdefghijklmnopqrstuvwxyz";
break;
}
}
if (cut) {
for (var i = cut.length - 1; i >= 0; i--) {
chars = chars.replace(cut[i], '');
}
}
var len = chars.length;
var result = "";
if (!strLen) {
strLen = Math.random(len);
}
var d = Date.parse(new Date());
for (var i = 0; i < strLen; i++) {
result += chars.charAt(Math.ceil(Math.random() * d) % len);
}
return result;
}
/**
* 程序主流程
* @param params
* @returns {boolean}
*/
function process(params) {
switch (params.mode) {
case 'server':
//检查定义的全局API是否存在
if (config.api && config.api.server && config.api.server in window) {
if (typeof window[config.api.server] === "function") {
window[config.api.server].call(this, params);
}
params.callback.call(this, params.quota);
} else {
var e = encodeURIComponent,
o = new Image();
//m=>quota msg
//u=>user agent
o.src = config.report + '?m=' + e(params.quota) + '&u=' + e(params.ua) + '&__r=' + randomChars(8);
o.onload = params.callback.call(this, params.quota);
o.onerror = function () {
if (!config.silence) {
alert('Connect Report Server Failed, Please Contact ' + config.admin + config.email);
}
};
}
break;
case 'user':
//展示数据
params.callback.call(this, params);
break;
}
return true;
}
/**
* 去掉页面标识,刷新页面
*/
function refresh(params) {
params = !params;
window.location.replace(window.location.href.replace(window.location.href.match(/localStorage=on/), params ? 'localStorage=disabled' : 'localStorage=off'));
}
/**
* 将之前处理过的数据展示出来
*/
function excel(data) {
//检查定义的全局API是否存在
if (config.api && config.api.user && config.api.user in window) {
if (typeof window[config.api.user] === "function") {
return window[config.api.user].call(this, data);
}
}
//执行默认操作
console.info('测试数据结果:');
console.log(data);
}
/**
* 正儿八经的处理过程
*
* 1.当请求地址包含标识(localStorage=on)的时候,测试并收集结果,代结果反馈完毕,去掉请求地址中的标识,刷新页面。
* 2.清除测试生成的缓存,报告用户情况。
*/
//获取UA
var ua = JSON.stringify(getUA.call(null)),
quota = false;
//当请求地址包含localStorage=on时
if (window.location.search.indexOf('localStorage=on') > -1) {
//如果浏览器支持localStorage的话
if (supportFeatures.call(null)) {
//测试并收集结果
quota = JSON.stringify(getQuota.call(null));
//将结果保存下来
localStorage.setItem('quota', quota);
}
//不论是否获取到quota, 都进行数据收集
return process({'quota': quota, 'ua': ua, 'callback': refresh, 'mode': 'server'});
} else { //当请求地址不包含localStorage=on的时候
if (supportFeatures.call(null)) {
//展示之前的消息
quota = localStorage.getItem('quota');
//清理所有数据
localStorage.clear();
}
//展示给用户采集结果
return process({'quota': quota, 'ua': ua, 'callback': excel, 'mode': 'user'});
}
})({"report": "http://www.localstorage.com/build/collect.php", "admin": "soulteary", "email": "@qq.com", "api": {"user": "pageReport"}, "silence": true});
```