JavaScript 堆内存不足 | s3.getObject 在 React/Node 应用中
JavaScript heap out of memory | s3.getObject in React/Node app
我们在 Digital Ocean 上托管了一个 React/Node 应用程序。我们还利用与 AWS S3 互操作的 Digital Ocean spaces 进行对象存储。基本上,该应用程序是一种内部保管箱。我们的管理员能够创建文件夹并将内容上传到这些文件夹。然后,我们的客户能够登录并下载我们允许他们访问的任何文件。
我们已成功将所有文件上传到 Digital Ocean Spaces。不管他们如何large/small。
问题是当我们尝试下载(作为管理员或客户端)任何大小超过 100MB 的内容时,我们会遇到 JavaScript 堆内存不足错误。此错误出现在系统后端。
我们尝试管理的一些解决方案是:
- 为 VM 提供更多 ram
- 增加浏览器内存 '--max-old-space-size'
- 启用 Digital Ocean CDN 通过边缘服务器流式传输内容
- 手动将文件数据流式传输到我们的后端,然后将其发送到前端
前端代码
downloadFile = (id, name, type) => {
axios
.get(
`/test-download/${id}`,
this.props.handleSnackBar(
"Your download has been started. Please wait."
)
)
.then(res => {
download(
new Blob([new Uint8Array(res.data.data.Body.data)]),
`${name}.${type}`
);
console.log(res);
console.log(res.data.data.Body),
this.props.handleSnackBar("Your download is now ready.");
})
.catch(err => console.log(err));
};
后端代码
app.get("/test-download/:id", (req, res) => {
var params = {
Bucket: bucketName,
Key: req.params.id
};
s3.getObject(params, function(err, data) {
//
console.log(data);
//
if (!err) {
res.send({ data, key: params.Key });
} else {
console.log({ err }); // an error occurred
}
});
});
带有流的后端代码
app.get("/test-download/:id", (req, res) => {
var params = {
Bucket: bucketName,
Key: req.params.id
};
// TRY
const fileRequest = s3.getObject(params);
let chunks = [];
fileRequest
.createReadStream()
.on("data", function(data) {
console.log(`Received ${data.length} bytes of data`);
chunks.push(data);
})
.on("end", function() {
console.log("no more data");
bufferData = Buffer.concat(chunks);
console.log(bufferData);
res.send({ bufferData, key: params.Key });
});
});
所以,基本上我有点卡住了。非常感谢可以提供的任何帮助。谢谢。
问题是,当您在最后一个片段中使用 streams
时,您缓冲了所有块,这违背了使用流的目的。
你应该做的是 .pipe
直接响应,这样使用的内存会很低。
app.get("/test-download/:id", (req, res) => {
const params = {
Bucket: bucketName,
Key: req.params.id
};
s3.getObject(params)
.createReadStream()
.pipe(res);
});
请记住,现在您没有响应 JSON
对象,因此应该更改客户端。
感谢 Marcos,我重新审视了我们尝试过的管道代码。但现在完全理解了我从 createReadStream().pipe()
接收到的原始数据响应,我能够转换数据。
前端代码
app.get("/test-download/:id", (req, res) => {
var params = {
Bucket: bucketName,
Key: req.params.id
};
s3.getObject(params)
.createReadStream()
.pipe(res)
.on("finish", () => {
console.log("** done");
});
});
后端代码
downloadFile = (id, name, type) => {
axios
.get(
`/test-download/${id}`,
{ responseType: "arraybuffer" },
this.props.handleSnackBar(
"Your download has been started. Please wait."
)
)
.then(res => {
console.log(res);
download(res.data, `${name}.${type}`);
this.props.handleSnackBar("Your download is now ready.");
})
.catch(err => console.log(err));
};
我们在 Digital Ocean 上托管了一个 React/Node 应用程序。我们还利用与 AWS S3 互操作的 Digital Ocean spaces 进行对象存储。基本上,该应用程序是一种内部保管箱。我们的管理员能够创建文件夹并将内容上传到这些文件夹。然后,我们的客户能够登录并下载我们允许他们访问的任何文件。
我们已成功将所有文件上传到 Digital Ocean Spaces。不管他们如何large/small。
问题是当我们尝试下载(作为管理员或客户端)任何大小超过 100MB 的内容时,我们会遇到 JavaScript 堆内存不足错误。此错误出现在系统后端。
我们尝试管理的一些解决方案是:
- 为 VM 提供更多 ram
- 增加浏览器内存 '--max-old-space-size'
- 启用 Digital Ocean CDN 通过边缘服务器流式传输内容
- 手动将文件数据流式传输到我们的后端,然后将其发送到前端
前端代码
downloadFile = (id, name, type) => {
axios
.get(
`/test-download/${id}`,
this.props.handleSnackBar(
"Your download has been started. Please wait."
)
)
.then(res => {
download(
new Blob([new Uint8Array(res.data.data.Body.data)]),
`${name}.${type}`
);
console.log(res);
console.log(res.data.data.Body),
this.props.handleSnackBar("Your download is now ready.");
})
.catch(err => console.log(err));
};
后端代码
app.get("/test-download/:id", (req, res) => {
var params = {
Bucket: bucketName,
Key: req.params.id
};
s3.getObject(params, function(err, data) {
//
console.log(data);
//
if (!err) {
res.send({ data, key: params.Key });
} else {
console.log({ err }); // an error occurred
}
});
});
带有流的后端代码
app.get("/test-download/:id", (req, res) => {
var params = {
Bucket: bucketName,
Key: req.params.id
};
// TRY
const fileRequest = s3.getObject(params);
let chunks = [];
fileRequest
.createReadStream()
.on("data", function(data) {
console.log(`Received ${data.length} bytes of data`);
chunks.push(data);
})
.on("end", function() {
console.log("no more data");
bufferData = Buffer.concat(chunks);
console.log(bufferData);
res.send({ bufferData, key: params.Key });
});
});
所以,基本上我有点卡住了。非常感谢可以提供的任何帮助。谢谢。
问题是,当您在最后一个片段中使用 streams
时,您缓冲了所有块,这违背了使用流的目的。
你应该做的是 .pipe
直接响应,这样使用的内存会很低。
app.get("/test-download/:id", (req, res) => {
const params = {
Bucket: bucketName,
Key: req.params.id
};
s3.getObject(params)
.createReadStream()
.pipe(res);
});
请记住,现在您没有响应 JSON
对象,因此应该更改客户端。
感谢 Marcos,我重新审视了我们尝试过的管道代码。但现在完全理解了我从 createReadStream().pipe()
接收到的原始数据响应,我能够转换数据。
前端代码
app.get("/test-download/:id", (req, res) => {
var params = {
Bucket: bucketName,
Key: req.params.id
};
s3.getObject(params)
.createReadStream()
.pipe(res)
.on("finish", () => {
console.log("** done");
});
});
后端代码
downloadFile = (id, name, type) => {
axios
.get(
`/test-download/${id}`,
{ responseType: "arraybuffer" },
this.props.handleSnackBar(
"Your download has been started. Please wait."
)
)
.then(res => {
console.log(res);
download(res.data, `${name}.${type}`);
this.props.handleSnackBar("Your download is now ready.");
})
.catch(err => console.log(err));
};