Example reading file paths using native node.js modules

This example flow uses the native Node.JS fs and path modules to read a list of file names having a specified file extension from a given folder.

You can pass the folder and extension strings in via the msg.payload. Output is a single message for each file found. The payload will be the full file path. Everything other than the payload will be passed through to each output msg.

You would actually probably be better off using a 3rd party module as indicated in the main code but this may be a useful example of using modules in a Node-Red function node.

Note that function nodes run in a "sandbox" and have restricted syntax. As such, you cannot do:

var fs = require('fs');

So you have to require the modules in your settings.js file instead:

...
    functionGlobalContext: { // enables and pre-populates the context.global variable
        // -- Pass in Libraries for convenience in function nodes -- //
        'path' : require('path'),
        'fs'   : require('fs')
    },
...

WARNING: Errors in the module callback functions WILL CRASH Node-Red - that is why the files.filter().foreach() code is wrapped in a try/catch

Here is the JavaScript in the function node complete with error checking:

// Sends a msg with full file path for each file in a given folder ending with given extension
// PARAMETERS:
//   msg.payload.folder {string} Folder to look in for files
//   msg.payload.ext    {string} The file extension to look for
// ASSUMPTIONS:
//   Node.JS libraries fs and path have been required in the functionGlobal section of settings.js
//   Tested using Node.JS v4.4.5 LTS and Node-Red 0.13.4
// NOTES:
//   You would almost certainly be better off using one of the "glob" modules such as glob-fs, node-glob or glob-all

var fs = global.get('fs1')
var path = global.get('path')

// Give up if fs or path have not been assigned to global context (see assumption 1)
if ( (typeof fs !== 'undefined') || (typeof path !== 'undefined') ) {
    node.error('Either fs or path modules could not be found in global context')
    return    
}
// And make sure they are functions
if ( (typeof fs !== 'object') || (typeof path !== 'object') ) {
    node.error('Either fs or path from global context is not an object & it must be')
    return    
}

var folder = msg.payload.folder || '/home/pi'
var extension = msg.payload.ext || '.gpx'

// Make sure the parameters are strings
if ( (typeof folder !== 'string') || (typeof extension !== 'string') ) {
    node.error('Either Folder or Ext is not a string, cannot process. Folder: ' + folder + ', Ext: ' + extension, err)
    return    
}

fs.readdir(folder, callback)

// readir is ASYNC so don't return msg here as
// it would be pointless

function callback(err, files) {
    if (err) {
        node.error('Oops! Folder does not exist: ' + folder, err)
        return
    }

    // Any error in here will crash Node-Red!! So catch them instead
    try {
        files
            .filter( filterOnExtension ) // calls the given callback func for each file, will only pass names where fn returns true
            .forEach( sendName )
    } catch (err) {
        node.error('Ouch! Something went badly wrong processing the files list', err)
        return
    }
}

function filterOnExtension(file) {
    var len = extension.length

    // Returns TRUE only if the file name ends in the given extension
    return file.substr(0-len) === extension
}

function sendName(file) {
        msg.payload = path.join(folder, file)
        node.send( msg )
}
[{"id":"b1bc15bb.e99be8","type":"inject","z":"106a5b82.ef95a4","name":"Inject Payload JSON","topic":"","payload":"{\"folder\":\"/home/pi\", \"ext\":\".sh\"}","payloadType":"json","repeat":"","crontab":"","once":false,"x":330,"y":4380,"wires":[["41f356dc.7b3c18"]]},{"id":"41f356dc.7b3c18","type":"function","z":"106a5b82.ef95a4","name":"Read folder, output file paths","func":"// Sends a msg with full file path for each file in a given folder ending with given extension\n// PARAMETERS:\n//   msg.payload.folder {string} Folder to look in for files\n//   msg.payload.ext    {string} The file extension to look for\n// ASSUMPTIONS:\n//   Node.JS libraries fs and path have been required in the functionGlobal section of settings.js\n//   Tested using Node.JS v4.4.5 LTS and Node-Red 0.13.4\n// NOTES:\n//   You would almost certainly be better off using one of the \"glob\" modules such as glob-fs, node-glob or glob-all\n\nvar fs = global.get('fs1')\nvar path = global.get('path')\n\n// Give up if fs or path have not been assigned to global context (see assumption 1)\nif ( (typeof fs !== 'undefined') || (typeof path !== 'undefined') ) {\n    node.error('Either fs or path modules could not be found in global context')\n    return    \n}\n// And make sure they are functions\nif ( (typeof fs !== 'object') || (typeof path !== 'object') ) {\n    node.error('Either fs or path from global context is not an object & it must be')\n    return    \n}\n\nvar folder = msg.payload.folder || '/home/pi'\nvar extension = msg.payload.ext || '.gpx'\n\n// Make sure the parameters are strings\nif ( (typeof folder !== 'string') || (typeof extension !== 'string') ) {\n    node.error('Either Folder or Ext is not a string, cannot process. Folder: ' + folder + ', Ext: ' + extension, err)\n    return    \n}\n\nfs.readdir(folder, callback)\n\n// readir is ASYNC so don't return msg here as\n// it would be pointless\n\nfunction callback(err, files) {\n    if (err) {\n        node.error('Oops! Folder does not exist: ' + folder, err)\n        return\n    }\n    \n    // Any error in here will crash Node-Red!! So catch them instead\n    try {\n        files\n            .filter( filterOnExtension ) // calls the given callback func for each file, will only pass names where fn returns true\n            .forEach( sendName )\n    } catch (err) {\n        node.error('Ouch! Something went badly wrong processing the files list', err)\n        return\n    }\n}\n\nfunction filterOnExtension(file) {\n    var len = extension.length\n\n    // Returns TRUE only if the file name ends in the given extension\n    return file.substr(0-len) === extension\n}\n\nfunction sendName(file) {\n        msg.payload = path.join(folder, file)\n        node.send( msg )\n}","outputs":1,"noerr":0,"x":569,"y":4380,"wires":[["60da0b63.227c54"]]},{"id":"60da0b63.227c54","type":"debug","z":"106a5b82.ef95a4","name":"","active":true,"console":"false","complete":"true","x":770,"y":4380,"wires":[]}]
TotallyInformation

Flow Info

created 1 year, 1 month ago

Node Types

Core
  • debug (x1)
  • function (x1)
  • inject (x1)

Tags

  • node.js
  • file-system
  • crash
  • error-check
  • modules
  • global
  • settings.js
  • function
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option