ファイルを読み取り、それを変更してthrough2
、同じファイルに書き込みたい、次のようなコード。
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)
})
})
}
上記のコードは機能しますが、もっとエレガントにできるか知りたいですか?
また、node-tempのような一時ライブラリも試します
残念ながら、同じファイルにreadStreamと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);
};
AVAは、それが機能することを証明するためにテストします。
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');
});
変更に関するいくつかのこと:
EMFILE
通常は問題になりません。特に最近では、ノードがファイル記述子についてよりスマートになっているためです。しかし、それがあなたにとって問題であるならば、そのライブラリはWindows上のウイルス対策ソフトウェアによって引き起こされる一時的なエラーを助けます。fs.rename()
。これはmv
コマンドラインと似ていますが、いくつかの微妙な違いがありますが、ここでは違いはそれほど重要ではありません。重要なのは、そこにあったファイルの名前を変更した後、一時パスには何も存在しないということです。Promise
いくつかのエッジケースを処理します。エラーの周り。開示:私はストリームの実装をで書きましたtemp-write
。:)全体として、これはまともな改善です。ただし、コメントで説明されている境界の問題が残っています。幸いなことに、この問題に遭遇したのはあなたが最初ではありません。実際のソリューションを特にエレガントとは言いませんが、自分で実装した場合は確かです。しかし、replacestreamはあなたを助けるためにここにあります。
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);
};
また...
一時ファイルは好きではありません
確かに、一時ファイルはしばしば悪いです。ただし、この場合、一時ファイルは適切に設計されたライブラリによって管理され、安全で邪魔にならない場所に保存されます。他のプロセスと競合する可能性はほとんどありません。また、rename()
何らかの理由で失敗した場合でも、ファイルはOSによってクリーンアップされます。
それはあなたが使用して完全に一時ファイルを避けることができると述べたfs.readFile()
とfs.writeFile()
の代わりにストリーミング。前者は、チャンクの境界について心配する必要がないため、テキストの置換もはるかに簡単になります。どちらかのアプローチを選択する必要がありますが、非常に大きなファイルの場合、ファイルを手動でチャンク化する以外に、ストリーミングが唯一のオプションである可能性があります。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加