有一个更优雅的方式来读取然后写入*节点jsstream相同的文件*

我想读取文件,然后改变它通过然后写入到相同的文件,代码如:

 const rm = require('rimraf') const through2 = require('through2') const fs = require('graceful-fs') // source file path const replacementPath = `./static/projects/${destPath}/index.html` // temp file path const tempfilePath = `./static/projects/${destPath}/tempfile.html` // read source file then write into temp file await promiseReplace(replacementPath, tempfilePath) // del the source file rm.sync(replacementPath) // rename the temp file name to source file name fs.renameSync(tempfilePath, replacementPath) // del the temp file rm.sync(tempfilePath) // promiseify readStream and writeStream function promiseReplace (readfile, writefile) { return new Promise((res, rej) => { fs.createReadStream(readfile) .pipe(through2.obj(function (chunk, encoding, done) { const replaced = chunk.toString().replace(/id="wrap"/g, 'dududud') done(null, replaced) })) .pipe(fs.createWriteStream(writefile)) .on('finish', () => { console.log('replace done') res() }) .on('error', (err) => { console.log(err) rej(err) }) }) } 

上面的代码工作,但我想知道我可以让它更优雅?

我也尝试一些像临时温度临时库

不幸的是,它不能将StreamStream和WriteStream读入到同一个文件中,于是我对此提出了一些问题 。

所以任何人都知道一个更好的方法来告诉我,非常感谢。

您可以通过消除不必要的依赖关系并使用新的简化构造函数来使代码更优雅。

 const fs = require('fs'); const util = require('util'); const stream = require('stream'); const tempWrite = require('temp-write'); const rename = util.promisify(fs.rename); const goat2llama = async (filePath) => { const str = fs.createReadStream(filePath, 'utf8') .pipe(new stream.Transform({ decodeStrings : false, transform(chunk, encoding, done) { done(null, chunk.replace(/goat/g, 'llama')); } })); const tempPath = await tempWrite(str); await rename(tempPath, filePath); }; 

testing

AVAtestingcertificate它的工作原理:

 import fs from 'fs'; import path from 'path'; import util from 'util'; import test from 'ava'; import mkdirtemp from 'mkdirtemp'; import goat2llama from '.'; const writeFile = util.promisify(fs.writeFile); const readFile = util.promisify(fs.readFile); const fixture = async (content) => { const dir = await mkdirtemp(); const fixturePath = path.join(dir, 'fixture.txt'); await writeFile(fixturePath, content); return fixturePath; }; test('goat2llama()', async (t) => { const filePath = await fixture('I like goats and frogs, but goats the best'); await goat2llama(filePath); t.is(await readFile(filePath, 'utf8'), 'I like llamas and frogs, but llamas the best'); }); 

关于这些变化的一些事情:

  • 通过2是不是真的需要了。 过去build立passthrough或正确转换stream是一个痛苦,但是由于简化的构build API,情况并非如此。
  • 你也许不需要优雅的FS。 除非你正在做大量的并发磁盘I / O,否则EMFILE通常不是一个问题,尤其是现在Node越来越聪明了。 但是,如果这对您来说是一个问题,那么该库确实可以帮助解决由Windows上的防病毒软件造成的临时错误。
  • 你绝对不需要这个rimraf。 你只需要fs.rename() 。 它与命令行上的mv类似,有一些细微的差别使得它不同,但这里的差异并不是特别重要。 重点是在那里的文件重命名临时path将没有什么。
  • 我使用临时写,因为它为您生成一个安全的随机文件path,并把它放在操作系统的临时目录(它自动得到清理,然后),再加上它处理转换为您的Promise和照顾一些边缘错误周围的案件。 披露:我写了temp-writestream实现。 🙂

总体而言,这是一个体面的改善。 但是,评论中仍然存在边界问题 。 幸运的是,你不是第一个遇到这个问题的人! 我不会说实际的解决scheme特别优雅,当然不是如果你自己实现它。 但替代stream是在这里帮助你。

 const fs = require('fs'); const util = require('util'); const tempWrite = require('temp-write'); const replaceStream = require('replacestream'); const rename = util.promisify(fs.rename); const goat2llama = async (filePath) => { const str = fs.createReadStream(filePath, 'utf8') .pipe(replaceStream('goat', 'llama')); const tempPath = await tempWrite(str); await rename(tempPath, filePath); }; 

也…

我不喜欢临时文件

事实上,临时文件往往是不好的。 但是,在这种情况下,临时文件由精心devise的库pipe理,并存储在安全的,不在位的位置。 几乎没有与其他stream程相冲突的机会。 即使rename()以某种方式失败,该文件也将被OS清除。

也就是说,你可以通过使用fs.readFile()fs.writeFile()而不是stream来完全避免临时文件。 前者还使得文本replace变得更容易,因为您不必担心块边界。 您必须select一种方法或另一种,但是对于非常大的文件,stream式传输可能是唯一的select,除了手动分块文件之外 。

在这种情况下,stream是无用的,因为它们会返回大块文件,可能会破坏正在search的string。 你可以使用stream,然后合并所有这些块来获得内容,然后replace你需要的string,但是这将是更长的代码,只会引发一个问题: why do you read file by chunks if you don't use them ?

达到你想要的最短的方法是:

 let fileContent = fs.readFileSync('file_name.html', 'utf8') let replaced = fileContent.replace(/id="wrap"/g, 'dududud') fs.writeFileSync('file_name.html', replaced) 

所有这些function都是synchronous ,所以你不必提出这些function