Создание читаемого потока для использования с FormData

Я использую API для загрузки файла CSV. Я создаю CSV-файл в памяти из String и загружаю его с помощью модуля request. Однако у меня возникают проблемы с созданием потока для чтения из строки. Я последовал ТАК-ответу на Как создавать потоки из строки в Node.Js. Вот мой код для этого решения:

var importResponse = function(csv, callback){
    stringify(csv, function(err, output){

        const s = new Readable();
        s._read = () => {}; 
        s.push(output);
        s.push(null);

        request.post({
          headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
          url: 'https://ca1.qualtrics.com/API/v3/responseimports',
          formData: {
            surveyId: 'SV_123',
            file: {
                value: s,
                options: {
                    contentType: 'text/csv; charset=utf-8'
                }
            }
          }
        }, function(err, res, body){
            if (err || res.statusCode !== 200){
              console.info(err || "Error status code: " + res.statusCode);
              console.info(body);
              return;
            }
        });
    });

}

Переменная csv выглядит как [["QID1","QID2"],["1","2"]], а вывод stringify выглядит как "QID1,QID2\n,1,2\n".

Это решение дает мне ошибку Unexpected end of input

{"meta":{"httpStatus":"400 - Bad Request","error":{"errorMessage":"Unexpected end of input"}}}

Если вместо этого я использую memfs, он отлично работает

const fs = require('memfs');

var importResponse = function(csv, callback){
    stringify(csv, function(err, output){
        // Create file in memory
        fs.writeFileSync('/data.csv', output); 

        request.post({
          headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
          url: 'https://ca1.qualtrics.com/API/v3/responseimports',
          formData: {
            surveyId: 'SV_123',
            file: {
                value: fs.createReadStream('/data.csv'),
                options: {
                    contentType: 'text/csv; charset=utf-8'
                }
            }
          }
        }, function(err, res, body){
            if (err || res.statusCode !== 200){
              console.info(err || "Error status code: " + res.statusCode);
              console.info(body);
              return;
            }
        });
    });

}

Как я могу преобразовать вывод stringify в поток, который я могу использовать для загрузки через api?

Просто чтобы мы знали наверняка, откуда берется stringify? У вас есть конкретный пакет, который вам нужен?

Jacob 19.07.2018 02:17

Похоже, csv-stringify может сам создавать потоки, к вашему сведению. Вероятно, вы можете просто передать stringify(csv) напрямую как "файловый" поток.

Jacob 19.07.2018 02:54

@Jacob ага, это исправило. Изменил свой код на var output = stringify(csv);, и теперь он работает. Также изменил модуль stringify на синхронный. Спасибо!

Eric 19.07.2018 03:21
Поведение ключевого слова "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) для оценки ваших знаний,...
2
3
4 027
2

Ответы 2

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

const s = new Readable({
  encoding: 'utf8',
  read(size) {
    // Possibly respect the requested size to make for a good consumer experience
    // Otherwise:
    this.push(output, 'utf8');
    this.push(null); // This signals that there's no more data.
  }
});

Вот как можно уважать пожелания читателя:

let data = output;
const s = new Readable({
  encoding: 'utf8',
  read(size) {
    let wantsMore = true;
    while (wantsMore) {
      const chunk = data.slice(0, size);          
      if (!chunk) {
        return void this.push(null);            
      }

      wantsMore = this.push(chunk, 'utf8');
      data = data.slice(size);
    }
  }
});

Спасибо за реплики. Я попытался добавить ваш фрагмент вместо предыдущего, но все равно получил ту же ошибку Unexpected end of input.

Eric 19.07.2018 02:09

Ой, я ошибся при сигнале EOF. Попробуй мое обновление.

Jacob 19.07.2018 02:19

Добавлены примечания по кодировке. Это странно.

Jacob 19.07.2018 02:33

Думаю, я понял это после того, как заметил, что вы используете request. Это странно с загрузкой "файлов". Смотрите другой ответ.

Jacob 19.07.2018 02:39

(оставим это пока на случай, если есть проблема с потоком, но, вероятно, это другой ответ).

Jacob 19.07.2018 02:42

Похоже, вы используете библиотеку request. Вы можете встретить это предостережение, как описано в их Прочти меня:

// Pass optional meta-data with an 'options' object with style: {value: DATA, options: OPTIONS}
// Use case: for some types of streams, you'll need to provide "file"-related information manually.
// See the `form-data` README for more information about options: https://github.com/form-data/form-data
custom_file: {
  value:  fs.createReadStream('/dev/urandom'),
  options: {
    filename: 'topsecret.jpg',
    contentType: 'image/jpeg'
  }
}

Поскольку вы используете нефайловый поток, простое указание фиктивного имени файла должно работать:

request.post({
  headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
  url: 'https://ca1.qualtrics.com/API/v3/responseimports',
  formData: {
    surveyId: 'SV_123',
    file: {
      value: s,
      options: {
        contentType: 'text/csv; charset=utf-8',
        filename: 'dummy.csv'
      }
    }
  }
}, function(err, res, body){
  if (err || res.statusCode !== 200){
    console.info(err || "Error status code: " + res.statusCode);
    console.info(body);
    return;
  }
});

Привет, Джейкоб, я попытался добавить имя файла (оно было там ранее), но оно все равно не работает. Интересно, а в Stringify проблема. Я подумал, что, возможно, это был символ новой строки в конце строки, но это не так. Я не уверен, что это проблема с Stringify, а memfs «устраняет» проблему при сохранении, или если сам поток неправильный и не обязательно контент ... если это имеет смысл.

Eric 19.07.2018 02:44

Да, это пакет. Извините должно было быть более ясным.

Eric 19.07.2018 02:51

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