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 )
}
i test this. i change settings.js like you mentioned and i put {"folder":"C:\Users\Public\img\tmp\", "ext":".pdf"} in inject node. when i deploy and start injecting i get this error
"Either fs or path modules could not be found in global context".