我目前正试图保存一个js对象与一些二进制数据和其他值。结果应该像这样:
{
"value":"xyz",
"file1":"[FileContent]",
"file2":"[LargeFileContent]"
}
直到现在我还没有二进制数据,所以我把所有的东西都保存在JSON中。对于二进制数据,我开始遇到大文件(>1GB)的问题。
我尝试了这个方法:JSON。字符串化或如何序列化二进制数据为base64编码的JSON?这对小大约20 mb的文件工作。然而,如果我使用这些大文件,那么FileReader的结果总是一个空字符串。结果如下所示:
{
"value":"xyz:,
"file1":"[FileContent]",
"file2":""
}
读取blobs的代码与另一篇文章中的代码非常相似:
const readFiles = async (measurements: FormData) => {
setFiles([]); //This is where the result is beeing stored
let promises: Array<Promise<string>> = [];
measurements.forEach((value) => {
let dataBlob = value as Blob;
console.log(dataBlob); //Everything is fine here
promises.push(
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(dataBlob);
reader.onloadend = function () {
resolve(reader.result as string);
};
reader.onerror = function (error) {
reject(error);
};
})
);
});
let result = await Promise.all(promises);
console.log(result); //large file shows empty
setFiles(result);
};
还有什么我可以试试的吗?
由于必须与其他计算机共享数据,因此必须生成自己的二进制格式。
显然,您可以按照自己的意愿制作,但考虑到您只是使用JSON字符串存储Blob对象的简单情况,我们可以提出一个非常简单的模式,我们首先存储有关我们存储的Blob的一些元数据,然后使用JSON字符串将每个Blob替换为UUID。
这是有效的,因为你遇到的限制实际上是字符串的最大长度,我们可以.slice()我们的二进制文件只读取它的一部分。由于我们从未将二进制数据读取为字符串,所以很好,JSON将只在我们有blob的地方保存UUID,并且它不应该增长太多。
下面是我很快做的一个实现,作为概念的证明:
/*
* Stores JSON data along with Blob objects in a binary file.
* Schema:
* 4 first bytes = # of blobs stored in the file
* next 4 * # of blobs = size of each Blob
* remaining = JSON string
*
*/
const hopefully_unique_id = "_blob_"; // <-- change that
function generateBinary(JSObject) {
let blobIndex = 0;
const blobsMap = new Map();
const JSONString = JSON.stringify(JSObject, (key, value) => {
if (value instanceof Blob) {
if (blobsMap.has(value)) {
return blobsMap.get(value);
}
blobsMap.set(value, hopefully_unique_id + (blobIndex++));
return hopefully_unique_id + blobIndex;
}
return value;
});
const blobsArr = [...blobsMap.keys()];
const data = [
new Uint32Array([blobsArr.length]),
...blobsArr.map((blob) => new Uint32Array([blob.size])),
...blobsArr,
JSONString
];
return new Blob(data);
}
async function readBinary(bin) {
const numberOfBlobs = new Uint32Array(await bin.slice(0, 4).arrayBuffer())[0];
let cursor = 4 * (numberOfBlobs + 1);
const blobSizes = new Uint32Array(await bin.slice(4, cursor).arrayBuffer())
const blobs = [];
for (let i = 0; i < numberOfBlobs; i++) {
const blobSize = blobSizes[i];
blobs.push(bin.slice(cursor, cursor += blobSize));
}
const pattern = new RegExp(`^${hopefully_unique_id}\d+$`);
const JSObject = JSON.parse(
await bin.slice(cursor).text(),
(key, value) => {
if (typeof value !== "string" || !pattern.test(value)) {
return value;
}
const index = +value.replace(hopefully_unique_id, "") - 1;
return blobs[index];
}
);
return JSObject;
}
// demo usage
(async () => {
const obj = {
foo: "bar",
file1: new Blob(["Let's pretend I'm actually binary data"]),
// This one is 512MiB, which is bigger than the max string size in Chrome
// i.e it can't be stored in a JSON string in Chrome
file2: new Blob([Uint8Array.from({ length: 512*1024*1024 }, () => 255)]),
};
const bin = generateBinary(obj);
console.log("as binary", bin);
const back = await readBinary(bin);
console.log({back});
console.log("file1 read as text:", await back.file1.text());
})().catch(console.error);