Use the core fs module to perform local filesystem operations.
Note: Each method also has a sync version, which should only be used on the first event loop tick, to avoid blocking the entire process and creating compounding latency across concurrent requests.
Note: Path arguments can be relative to process.cwd() or absolute.
Note: Core APIs use callbacks by default. Since callbacks are a known anti-pattern, all the guides use promises (via songbird) instead. Further, examples are assumed to be contained within the context an async function where await is available.
let msg = 'Hello, World!'
await fs.promise.writeFile('helloworld.txt', msg)
console.log('File created')
Using Streams:
let msg = 'Hello, World!'
let stream = fs.createWriteStream('helloworld.txt')
stream.write(msg);
stream.end();
let data = await fs.promise.readFile('helloworld.txt', 'utf8')
console.log(data)
Here we set the encoding to utf8 so the file content returns as a string.
Using Stream:
let readStream = fs.createReadStream('helloworld.txt')
stream.on('data', 'utf8', data => console.log(data))
stream.on('end', () => stream.close())
Updating files is easy because the above methods for creating files automatically overwrite any content if executed with an existing file path. Other methods for updating files include:
Append contents to the end of a file with fs.appendFile:
let msgToAppend = '\nHow are you?'
await fs.promise.appendFile('helloworld.txt', msgToAppend)
console.log('File appended')
let oldPath = 'helloworld.txt'
let newPath = 'heyworld.txt'
await fs.promise.rename(oldPath, newPath)
console.log(`${oldPath} renamed ${newPath}`)
await fs.promise.unlink('helloworld.txt')
console.log('File deleted')
fs.statReturns a Stats object with meta data including size and last accessed and modified dates. Stats also includes helper methods (e.g., .isFile() and .isDirectory()):
let stat = await fs.promise.stat('foo/bar')
if (stat.isDirectory()) {
// do work
}
fs.watchfs.watch (and fs.watchFile) tracks changes made in a given file or directory, and returns a rename or change event:
fs.watch('path/to/file/or/dir', (event, filename) => {
if (event === 'rename') {
console.log('File was renamed or deleted')
} else if (event === 'change') {
console.log('File was changed')
}
})
However, fs.watch is well known to be unreliable across different platforms. For example, the filename callback parameter is actually not reported on OSX. Furthermore, often simple changes are reported as rename events.
rimrafrimraf is a useful package that allows non-empty directories to be deleted, while fs.unlink does not. It also allows recursive deletion, so be careful not to accidentally erase the file you're working on.
let rimraf = require('rimraf')
// In an async function...
await rimraf.promise('path/to/directory')
console.log('Directory deleted')
chokidarchokidar is a package that wraps fs.watch and fs.watchFile and effectively resolves their unreliability:
let chokidar = require('chokidar')
let watcher = chokidar.watch('.', {ignored: 'node_modules'})
watcher.on('all', (event, path, stat) => {
console.log(event, path);
if (event === 'change') {
console.log(stat)
}
})
This example assigns a watcher to the entire current directory ('.'), to register all events, ignoring node_modules/.
add, addDir, change events each return the same stats object as fs.stat above.
Additional files can easily be added to the watcher (in cases when not all files are already being watched) with:
watcher.add('addition.txt')
And removed:
watcher.unwatch('someFile.js')
fs-extrafs-extra wraps fs to provide additional methods Can be used in place of fs:
let fs = require('fs-extra')
Supported methods: copy, copySync, createOutputStream, emptyDir, emptyDirSync, ensureFile, ensureFileSync, ensureDir, ensureDirSync, mkdirs, mkdirsSync, move, outputFile, outputFileSync, outputJson, outputJsonSync, readJson, readJsonSync, remove, removeSync, writeJson, writeJsonSync
