JavaScript堆内存不足| React / Node应用中的s3.getObject

时间:2019-04-01 17:22:24

标签: javascript node.js reactjs amazon-s3 aws-sdk

我们在Digital Ocean上托管了一个React / Node应用程序。我们还将利用可与AWS S3互操作的Digital Ocean空间进行对象存储。基本上,该应用程序有点像内部保管箱。我们拥有能够创建文件夹并将内容上传到这些文件夹的管理员。然后,我们的客户便能够登录并下载我们允许他们访问的任何文件。

我们成功地将所有文件上传到Digital Ocean Spaces。不管它们有多大/小。

问题在于,当我们尝试以管理员或客户端身份下载任何大小超过100MB的内容时,会遇到JavaScript堆内存不足错误。此错误出现在系统的后端。

我们尝试管理的一些解决方案是:

  1. 为VM提供更多的内存
  2. 增加浏览器的内存'--max-old-space-size'
  3. 使Digital Ocean CDN能够通过边缘服务器流式传输内容
  4. 手动将文件数据流式传输到我们的后端,然后将其发送到前端

前端代码

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 });
  });

});

所以,基本上我有点卡住了。可以提供的任何帮助将不胜感激。谢谢。

2 个答案:

答案 0 :(得分:0)

问题在于,当您在最后一个片段上使用streams时,会缓冲所有块,从而不利于使用流。

您应该直接在响应中.pipe,这样使用的内存将非常低。

app.get("/test-download/:id", (req, res) => {
    const params = {
        Bucket: bucketName,
        Key: req.params.id
    };

    s3.getObject(params)
        .createReadStream()
        .pipe(res);

});

请记住,现在您没有响应JSON对象,因此应该更改客户端。

答案 1 :(得分:0)

感谢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));
};