使用 JavaScript 将 localStorage 填充到最大容量

598 阅读1分钟

检测 localStorage 何时已满的唯一方法是在调用时使用 QuotaExceededError(localStorage.setItem), 最初的“MVP”想法是编写一个循环,逐字节增加 localStorage 大小直到它已满

例如:

try {
  for (let i = 1; i < Infinity; i++) {
    localStorage.setItem("__test__", "x".repeat(i));
  }
} catch (err) {}

但是,循环写入并不是一个实际的选择。它的效率非常低,很容易使浏览器崩溃。

因此,受这个 StackOverflow 答案的启发,我选择先存储较大的字符串,然后存储较小的字符串,直到 localStorage 填满:

/*
将localStorage填满其最大容量。
首先,我们用100个字符的块填充localStorage,直到它
命中配额超出异常。
然后,我们再次使用1个字符的块。
最后,我们再一次逐个字符,以确保localStorage是完全充满。
要清除localStorage,你可以使用localStorage.clear()。不过,以防万一你想清理仅存储在这个函数的数据(可能是因为你想要在运行之前保存在localStorage中的东西),
我们返回一个方便的清理函数。
@return一个清理函数来删除我们存储的数据。
* /
function fillLocalStorage(): () => void {
  function storeIncreasinglyBigItem(
    key: string,
    charactersIncrement: number
  ): void {
    const MAX_ITERATIONS = 10_000; // Safeguard against OOM & crashes
    for (let i = 1; i <= MAX_ITERATIONS; i++) {
      localStorage.setItem(key, "x".repeat(i * charactersIncrement));
    }
  }
  try {
    storeIncreasinglyBigItem("__1", 100_000);
  } catch (_err1) {
    try {
      storeIncreasinglyBigItem("__2", 1_000);
    } catch (_err2) {
      try {
        storeIncreasinglyBigItem("__3", 1);
      } catch (_err3) {
        // 存储空间现在已经完全满了
      }
    }
  }
  return function cleanup() {
    localStorage.removeItem("__1");
    localStorage.removeItem("__2");
    localStorage.removeItem("__3");
  };
}

请注意,此解决方案有一个很小的边缘情况,fillLocalStorage即没有 100% 填充 localStorage:由于浏览器确定 localStorage 大小的值和,在极少数情况下,报告存储已满的时候可能还剩下大约 3 个字节的大小。

上面的代码片段肯定是可以优化的,有一个更优雅的方法:首先找到最高顺序的位,然后按递减顺序测试每个位,直到localStorage满了。

这种方法比我上面建议的方法性能更好,因为它涉及更少的分配和迭代,它使用单个localStorage项,还解决了我刚才提到的边缘情况。

function fillLocalStorage(): () => void {
  const key = "__filling_localstorage__";

  let max = 1; // This holds the highest order bit.
  let data = "x"; // 我们要存储在localStorage中的字符串。

  // Find the highest order bit.
  try {
    while (true) {
      data = data + data;
      localStorage.setItem(key, data);
      max <= 1;
    }
  } catch {}

  // 通过增加相反的字符串长度来填充剩余的空间
  for (let bit = max > 1; bit > 0; bit >= 1) {
    try {
      localStorage.setItem(key, data.substring(0, max , bit));
      max = bit;
    } catch {
      // 存储空间现在完全满了
    }
  }

  // 清理
  return function cleanup() {
    localStorage.removeItem(key);
  };
}