0

Based on other questions (below) and the docs I have the following which should create a new directory and file when it doesn't exist, or replace it when it does:

require('fs')

;(async ()=>{

//ref 1
fs.closeSync(fs.openSync('./newpath/newfile', 'w')); // make sure path and file exists
let mystream = fs.createWriteStream('./newpath/newfile',{encoding:'binary',flags : 'w'})

//ref2
await new Promise(r=> mystream.on('open'),(r)=>{r()})
let whyohwhy = Buffer.from("Should this be easy?")
mystream.write(whyohwhy,'binary',e=>console.log('Written to ./newpath/newfile'))

})();

ref1: https://stackoverflow.com/a/12809419/1461850

ref2: https://stackoverflow.com/a/12906805/1461850

Other "nearly" questions:

File and folders create if not exist

Creating a file only if it doesn't exist in Node.js

Create a file if it doesn't already exist

Alas I get this error

Promise {
  <rejected> Error: ENOENT: no such file or directory, open './newpath/newfile'
      at Object.openSync (fs.js:498:3)
      at REPL10:3:17
      at REPL10:9:3
      at Script.runInThisContext (vm.js:133:18)
      at REPLServer.defaultEval (repl.js:486:29)
      at bound (domain.js:416:15)
      at REPLServer.runBound [as eval] (domain.js:427:12)
      at REPLServer.onLine (repl.js:819:10)
      at REPLServer.emit (events.js:388:22)
      at REPLServer.emit (domain.js:470:12) {
    errno: -4058,
    syscall: 'open',
    code: 'ENOENT',
    path: './newpath/newfile'
  }
}
> (node:13988) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open './newpath/newfile'
    at Object.openSync (fs.js:498:3)
    at REPL10:3:17
    at REPL10:9:3
    at Script.runInThisContext (vm.js:133:18)
    at REPLServer.defaultEval (repl.js:486:29)
    at bound (domain.js:416:15)
    at REPLServer.runBound [as eval] (domain.js:427:12)
    at REPLServer.onLine (repl.js:819:10)
    at REPLServer.emit (events.js:388:22)
    at REPLServer.emit (domain.js:470:12)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:13988) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:13988) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.


Is there a simple/canonical way of creating a write initalised writeStream that creates the path/file if non existent or replaces it if it exists?



Lee
  • 29,398
  • 28
  • 117
  • 170

2 Answers2

1

Just came upon this. What I did was to extend Writable like this:

    class AsyncPathMakerWriteStream extends Writable {
    constructor (basePath, fileName) {
        super()
        this.basePath = basePath
        this.fileName = fileName
        this.fd = null
    }
    _construct (callback) {
        fs.mkdir(this.basePath, { recursive: true }, err => {
            if (err) {
                callback(err)
            } else {
                fs.open(join(this.basePath, this.fileName), (err, fd) => {
                    if (err) {
                        callback(err)
                    } else {
                        this.fd = fd
                        callback()
                    }
                })
            }
        })
    }
    _write (chunk, encoding, callback) {
        fs.write(this.fd, chunk, callback)
    }
    _destroy (err, callback) {
        if (this.fd) {
            fs.close(this.fd, (er) => callback(er || err))
        } else {
            callback(err)
        }
    }
}

Data will be internally buffered in the stream until the file is open without blocking the main thread.

If all you need to do is to ensure the path exists, perhaps extending Writable is a bit too much.

Here's another solution:

const { PassThrough } = require('node:stream')
const fs = require('node:fs')

const mystream = new PassThrough()

// you can now start writing to mystream, data will be buffered
mystream.write('ho ho ho')

ensurePathExistsAndOtherAsyncStuff()
    .then(() => {
        const fileWriteStream = fs.createWriteStream('./newpath/newfile',{encoding:'binary',flags : 'w'})
        mystream.pipe(fileWriteStream, { 
            end: true // closing mystream will close fileWriteStream
        })
    })
Juan Cortines
  • 123
  • 1
  • 7
0

I managed to do this with the fs-extra module's ensureFile function:

let fs = require('fs-extra')

;(async ()=>{

    await fs.ensureFile('./newpath/newfile').catch(err=>console.log)
    let mystream = fs.createWriteStream('./newpath/newfile',{encoding:'binary',flags : 'w'})
    let whyohwhy = Buffer.from("Why can't this just be easy!")
    mystream.write(whyohwhy,'binary',e=>console.log('Written to ./newpath/newfile'))

})();

However I'd be interested in other ways to do it...

Lee
  • 29,398
  • 28
  • 117
  • 170