Чтение больших файлов в Node.js с другого сервера

У меня есть два сервера, которые общаются друг с другом. Server1 запрашивает части файла у Server2 и сохраняет полученные данные в один файл. Предполагается, что Server2 получит каждый из этих запросов и создаст поток данных по конвейеру.

Предположим, что файлы, хранящиеся (каталог) на Сервере 2, выглядят следующим образом

bigfile.gz
   bigfile.gz.part-0
   bigfile.gz.part-1
   bigfile.gz.part-2
   ......

Таким образом, Server1 отправит запрос на часть 0, затем на часть 1 и так далее на Server2. Отсюда и использование цикла для выполнения запросов.

Сервер 1 (фрагмент кода)

for (var i in requestInfo['blockName']) {
            var blockName = i;
            var IP = requestInfo['blockName'][i][0];
            var fileData = JSON.stringify({
                blockName: blockName,
                fileName: requestInfo['fileName']
            });
            makeRequest(fileData, IP);
            console.info(counter);
 }

    function makeRequest(fileData, IP) {
        var options = {
            host: IP,
            port: 5000,
            path: '/read',
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            }
        };

        var req = http.request(options, function(res) {
            var data = '';
            res.on('data', function(chunk) {
                data += chunk;
            });

            res.on('end', function() {
                console.info(data.length);
                //fs.appendFileSync(fileName, data);
                var writeStream = fs.createWriteStream(fileName, { "flags": 'a' });
                writeStream.write(data);
                writeStream.end();
            });
        });

        req.write(fileData); 
        req.end();
    }

Сервер 2 (фрагмент кода)

app.post('/read', function(req, res) {
    var dataBody = req.body;
    fs.createReadStream(dataBody.fileName + '/' + dataBody.blockName).pipe(res);
});

Тот, что выше, работает, когда я тестирую его с текстовым файлом размером 100 МБ. Но это не удается, когда у меня есть файл .gz размером 1 ГБ или даже когда я тестирую его с файлом .zip, выходной файл .zip, созданный на стороне Сервера 1, имеет неправильный размер.

Я не уверен, что я здесь делаю не так, или это альтернативное решение

Обновлено:

Также мой Server1 вылетает при работе с большим файлом .gz размером 1 ГБ

Вы обрабатываете все содержимое как текст - вот почему он отлично работает с текстовым файлом, но не с двоичным файлом! Нашел это, который может вам помочь. Также, вероятно, ваш ответ - это.

Jamiec 14.03.2018 08:55

Я перешел по вашим предложенным ссылкам, но вижу эту ошибку TypeError: аргумент «список» должен быть массивом буферов, даже если я передаю массив

RRP 14.03.2018 09:27

@Jamiec теперь работает, я устанавливал res.setencoding (..), это не нужно. Но файл размером 1 ГБ вызывает сбой приложения

RRP 14.03.2018 09:32

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

Alex Michailidis 14.03.2018 13:52

@ alex-rokabilis не могли бы вы привести пример

RRP 14.03.2018 17:48
Поведение ключевого слова "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
5
807
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Основная проблема здесь в том, что вы обрабатываете свои данные как строку, добавляя к ней chunks.

Переписав это, должно быть

var req = http.request(options, function(res) {
  var data = [];
  res.on('data', function(chunk) {
    data.push(chunk);
  });

  res.on('end', function() {
    fs.writeFile(fileName, Buffer.concat(data), function() {
      console.info("write end")
    });
  });
});

Таким образом, мы создаем массив двоичных фрагментов большой, и по завершении загрузки записываем конкатенацию всех фрагментов в файл.

Но обратите внимание на слово большой

Если вы будете придерживаться этой реализации, вы рискуете получить нехватку памяти, особенно если вы имеете дело с большими (> 500 МБ) файлами.

Потоки на помощь

var req = https.request(options, function(res) {
  res.pipe(fs.createWriteStream(fileName)).on("close", function() {
    console.info("write end");
  });
});

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

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