Недостаточно памяти для кучи JavaScript | s3.getObject в приложении React/Node

У нас есть приложение React/Node, размещенное в Digital Ocean. Мы также используем пространство Digital Ocean, которое совместимо с AWS S3 для хранения объектов. По сути, приложение представляет собой своего рода внутренний дропбокс. У нас есть администраторы, которые могут создавать папки и загружать контент в эти папки. Затем у нас есть клиенты, которые могут войти в систему и загрузить любые файлы, к которым мы разрешаем им доступ.

Нам удалось загрузить все файлы в Digital Ocean Spaces. Неважно, насколько они большие/маленькие.

Проблема заключается в том, что когда мы пытаемся загрузить (как администратор или клиент) любой контент размером более 100 МБ, мы сталкиваемся с ошибкой JavaScript Heap of memory. Эта ошибка появляется на бэкенде системы.

Вот некоторые решения, которые мы пытались администрировать:

  1. Предоставление виртуальной машине большего количества оперативной памяти
  2. Увеличьте память браузера '--max-old-space-size'
  3. Включение CDN Digital Ocean для потоковой передачи контента через пограничные серверы
  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.info(res);

    console.info(res.data.data.Body),
      this.props.handleSnackBar("Your download is now ready.");
  })
  .catch(err => console.info(err));
};

Внутренний код

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

s3.getObject(params, function(err, data) {
  //
  console.info(data);
  //
  if (!err) {

    res.send({ data, key: params.Key });
  } else {
    console.info({ 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.info(`Received ${data.length} bytes of data`);
    chunks.push(data);
  })
  .on("end", function() {
    console.info("no more data");
    bufferData = Buffer.concat(chunks);
    console.info(bufferData);
    res.send({ bufferData, key: params.Key });
  });

});

В общем, я застрял. Любая помощь, которая может быть предложена, приветствуется. Спасибо.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
1 283
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема в том, что когда вы используете streams в последнем фрагменте, вы буферизуете все фрагменты, нарушая цель использования потоков.

Вместо этого вы должны сделать .pipe прямо к ответу, таким образом используемая память будет довольно низкой.

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

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

});

Имейте в виду, что сейчас вы не отвечаете на объект JSON, поэтому клиент должен быть изменен.

Это объяснило бы интересные данные ответа :) Итак, теперь код на стороне клиента должен преобразовать это в json?

ColeBear 02.04.2019 18:44

Зачем вам нужно конвертировать его в JSON? Вы используете new Uint8Array(res.data.data.Body.data) во внешнем интерфейсе. просто используйте new Uint8Array(res.data) или new Uint8Array(res.data.data) (я не знаю, как получить доступ к ответу в этой библиотеке)

Marcos Casagrande 02.04.2019 18:47

Я вижу следующие данные: "�0�wO��]j��hW��A�Ap���:��<B^�tS�t+��"���/�n��3Qҕ �$�=?�q*�m&‌​;:���h�ZȆY��K3Ʃ�2 뗷�K�(5" Uint8Array(res.data) просто возвращает пустой массив. Аналогично, new Blob([new Uint8Array(res.data)]) возвращает пустой объект.

ColeBear 02.04.2019 18:53

Это необработанный файл, который вы загружаете. Нажмите URL-адрес без аксиом, прямо в браузере, вы получите загруженный файл. Вы можете установить соответствующие заголовки для загрузки, content-disposition и так далее.

Marcos Casagrande 02.04.2019 18:55

Указать вам где-нибудь для чего именно? Что произойдет, если вы просто войдете в :/test-download/54 в браузере?

Marcos Casagrande 02.04.2019 19:35

Сам URL-адрес не будет работать, потому что это частный файл. Вот почему мы используем s3 с параметрами ведра.

ColeBear 02.04.2019 19:54

Я не говорю URL-адрес s3, я имею в виду URL-адрес вашего маршрута... тот, который я написал в сценарии.

Marcos Casagrande 02.04.2019 19:56
Ответ принят как подходящий

Благодаря Маркосу я пересмотрел код конвейера, который мы пробовали. Но теперь, полностью понимая ответ необработанных данных, который я получил от 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.info("** 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.info(res);
    download(res.data, `${name}.${type}`);
    this.props.handleSnackBar("Your download is now ready.");
  })
  .catch(err => console.info(err));
};

То, что называется «Frontend code», на самом деле выглядит как «Backend code» и наоборот.

Eliott Paris 20.11.2020 16:25

Другие вопросы по теме