同步存储、每个项目的最大配额字节和分块数据存储

Gau*_*don 4 javascript google-chrome-extension

我正在使用此代码的修改版本(更新:此答案已更新为使用正确的代码,但此问题仍然有价值,因为它包含有关此问题的测试用例和讨论)在字符串化后存储单个对象同步存储中的分块键。 请注意,同步存储具有每个项目的最大配额大小。所以,我有这些maxLengthPerItemmaxValueLength变量。

function lengthInUtf8Bytes(str) {
    // by: https://stackoverflow.com/a/5515960/2675672
    // Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence.
    var m = encodeURIComponent(str).match(/%[89ABab]/g);
    return str.length + (m ? m.length : 0);
}

function syncStore(key, objectToStore, callback) {
    var jsonstr = JSON.stringify(objectToStore), i = 0, storageObj = {},
        // (note: QUOTA_BYTES_PER_ITEM only on sync storage)
        // subtract two for the quotes added by stringification     
        // extra -5 to err on the safe side
        maxBytesPerItem = chrome.storage.sync.QUOTA_BYTES_PER_ITEM - NUMBER, 
        // since the key uses up some per-item quota, use
        // "maxValueBytes" to see how much is left for the value
        maxValueBytes, index, segment, counter; 

    console.log("jsonstr length is " + lengthInUtf8Bytes(jsonstr));

    // split jsonstr into chunks and store them in an object indexed by `key_i`
    while(jsonstr.length > 0) {
        index = key + "_" + i++;
        maxValueBytes = maxBytesPerItem - lengthInUtf8Bytes(index);

        counter = maxValueBytes;
        segment = jsonstr.substr(0, counter);           
        while(lengthInUtf8Bytes(segment) > maxValueBytes)
            segment = jsonstr.substr(0, --counter);

        storageObj[index] = segment;
        jsonstr = jsonstr.substr(counter);
    }
    // later used by retriever function
    storageObj[key] = i;
    console.log((i + 1) + " keys used (= key + key_i)");
    // say user saves till chunk 20 in case I
    // in case II, user deletes several snippets and brings down
    // total no. of "required" chunks to 15; however, the previous chunks
    // (16-20) remain in memory unless they are "clear"ed.
    chrome.storage.sync.clear(function(){                       
        console.log(storageObj);
        console.log(chrome.storage.sync);
        chrome.storage.sync.set(storageObj, callback);          
    });
}
Run Code Online (Sandbox Code Playgroud)

问题出在这一行:

maxLengthPerItem = chrome.storage.sync.QUOTA_BYTES_PER_ITEM - NUMBER,
Run Code Online (Sandbox Code Playgroud)

问题是 5 是没有错误的最小值 NUMBER。这是您可以用来测试我的理论的示例代码:

var len = 102000,
    string = [...new Array(len)].map(x => 1).join(""),
    Data = {
        "my_text": string
    },
    key = "key";

syncStore(key, Data, function(){
    console.log(chrome.runtime.lastError && chrome.runtime.lastError.message);
});
Run Code Online (Sandbox Code Playgroud)

使用 4 个收益率MAX_QUOTA_BYTES_PER_ITEM超过错误。您可以自己调整len(to 20000, 60000<102000等) 的值来检查我的理论。

题:

为什么当前方法需要exactly 5最小值?我知道字符串化有两个引号,但其他 3 个字符呢?他们从哪里来?

此外,我注意到在像这样的文本中 ,甚至不起作用。在上述特定情况下,所需的最小 NUMBER 为.Data56

澄清:

我的问题的重点不是同步存储数据的其他方法是什么。

我的问题的重点是为什么当前的方法需要exactly 5(以及为什么文本数据需要 6。)恕我直言,我的问题非常具体,肯定不值得投票。

更新:我添加了基于 UTF-8 字节长度测量存储数据的新代码,但它仍然没有提供理想的结果。我还添加了代码以更轻松地测试我的理论。

aps*_*ers 5

问题是 ChromeJSON.stringify在存储之前应用到每个字符串块,这会\在第一个字符串中添加三个字符(添加到已知的 2 外引号中,构成完整的 5)。Chromium 源代码中记录了这种行为:(Calculate the setting size based on its JSON serialization size并且实现确实根据 计算大小key.size() + value_as_json.size())。

也就是说,里面的值key_0是字符串

{"my_text":"11111111...
Run Code Online (Sandbox Code Playgroud)

但它存储为

"{\"my_text\":\"11111111..."
Run Code Online (Sandbox Code Playgroud)

您需要考虑两个外部引号的原因与您需要考虑添加的斜杠的原因相同。两者都表示JSON.stringify对字符串输入进行操作的输出。

您可以通过执行以下操作确认转义斜杠是问题

var jsonstr = JSON.stringify(objectToStore).replace(/"/g,"Z")
Run Code Online (Sandbox Code Playgroud)

并观察到所需的NUMBER偏移量2不是5,因为{Zmy_textZ:Z11111...没有额外的斜线。

我没有仔细查看,但 Lorem 文本包含一个换行符和一个选项卡(请参阅:)id faucibus diam.\,您JSON.stringify(正确地)将其转换为,\n\t但 Chrome 的附加内容stringify进一步扩展为\\n\\t,额外的 2 个字节您没有考虑在内。如果它与其他两个引号或其他可转义字符分块,则可能会导致一个包含 4 个未说明字节的块。

这里的解决方案是考虑 Chrome 将在存储时进行的转义。我建议在评估它是否太大时应用JSON.stringify到每个segment,以便分块算法消耗正确的字节数。然后,一旦你决定了一个不会引起问题的大小,即使在被双字符串化之后,从常规字符串中消耗那么多字节。就像是:

while(lengthInUtf8Bytes(JSON.stringify(segment)) > maxValueBytes)
    ...
Run Code Online (Sandbox Code Playgroud)

请注意,这将自动考虑来自外部引号的两个字节,因此甚至不需要进行QUOTA_BYTES_PER_ITEM - NUMBER计算。用您提出的术语来说,使用这种方法,NUMBER0.