Node.js преобразует поток файлов и записывает в тот же файл, в результате получается пустой файл

Я пытаюсь изменить некоторые файлы, используя потоки файлов узла и пользовательские функции преобразования. Это функция преобразования:

const TransformStream = function() {
  Transform.call(this, {objectMode: true});
};
util.inherits(TransformStream, Transform);

TransformStream.prototype._transform = function(chunk, encoding, callback) {
  let line = chunk.toString()
  if (!this.findLinesMode && lineStartRe.test(line)) {
    this.findLinesMode = true
    this.lines = []
  }
  if (this.findLinesMode) {
    this.lines.push(line)
  }
  if (this.findLinesMode && lineEndRe.test(line)) {
    this.findLinesMode = false
    line = this.lines.join('').replace(re, (str, match) => match.trim())
  }
  if (!this.findLinesMode) {
    this.push(line + '\n')
  }

  callback()
};

И я попытался использовать его в следующем коде:

byline(fs.createReadStream(filePath, {encoding: 'utf8'}))
  .pipe(new TransformStream())
  .pipe(fs.createWriteStream(filePath))

Тем не менее, файл заканчивается пустым.

Я уверен, что код преобразователя работает, как и ожидалось, потому что я попытался передать его process.stdout и вывод именно так, как я хочу.

У меня вопрос: что я делаю не так и что я могу попытаться исправить?

2 ответа

Решение

Это не проблема с вашим кодом преобразователя, а проблема в том, что вы открываете файл для записи, который вы перезаписываете, вероятно, еще до того, как что-то прочитаете из него.

Это было бы то же самое в оболочке. Если вы запускаете:

cat < file.txt > file.txt

или же:

tr a-z A-Z < x.txt > x.txt

это приведет к тому, что файл станет пустым.

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

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

Используя предложение @rsp, мы можем использоватьtmppackage из NPM, чтобы создать временный файл и записать в него запись после преобразования. Следующий шаг — скопировать этот файл в исходный файл.

      const fs = require("fs");
const {Transform} = require("stream");
const tmp = require("tmp")
const readStream = fs.createReadStream(this.databasePath);
const tmpFile = tmp.fileSync({'postfix' : '.json'});
const writeStream = fs.createWriteStream(tmpFile.name);
const filePath = this.databasePath;
try{
      readStream.pipe(
        new Transform({
          transform(chunk, encoding, callback){
            let fileData = JSON.parse(chunk);
            fileData.data = fileData.data.map((_doc, _index) => {
              let updatedDoc = _doc;
              if(_doc._id===_id){
                updatedDoc = {..._doc,[ _field] : _value};
              }
              return updatedDoc;
            });

        callback(null,JSON.stringify(fileData));
      }
    })
  ).pipe(writeStream).on('finish', () =>{
    return fs.copyFile(tmpFile.name, filePath, (err) =>{
      if(err) {
        throw err;
      }
    })
  });
  this.data = this.updatedData;
}
catch(error){
  // undo the changes in the local copy
  this.updatedData = this.data;
  return this._handleData(error, null);
}

Это пример, показывающий, как использовать пакет tmp для обновления исходного файла с использованием временной копии.

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