How to access the folder and sub-folder of an FTP server

I wanted to access the sub-folders of an FTP server (created automatically by my surveillance cameras), but the node https://flows.nodered.org/node/node-red-contrib-ftp only displayed the folders root.

@afelix found a proposal to modify the original node on the Github: https://github.com/joeartsea/node-red-contrib-ftp/issues/19

You must therefore open the two files found in (RPI example): /home/pi/.node-red/node_modules/node-red-contrib-ftp

  1. Copy / paste in ftp.html :
<script type="text/x-red" data-template-name="ftp">
  <div class="form-row">
    <label for="node-config-input-host"><i class="fa fa-bookmark"></i> Host</label>
    <input type="text" id="node-config-input-host" placeholder="localhost" style="width: 40%;" />
    <label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
    <input type="text" id="node-config-input-port" placeholder="21" style="width:45px">
  </div>
  <div class="form-row">
    <label for="node-config-input-user"><i class="fa fa-user"></i> User</label>
    <input type="text" id="node-config-input-user" placeholder="anonymous" />
  </div>
  <div class="form-row">
    <label for="node-config-input-password"><i class="fa fa-lock"></i> Password</label>
    <input type="password" id="node-config-input-password">
  </div>
  <div class="form-row">
    <label for="node-config-input-connTimeout"><i class="fa fa-clock-o"></i> Connection Timeout</label>
    <input type="text" id="node-config-input-connTimeout" placeholder="10000">
  </div>
  <div class="form-row">
    <label for="node-config-input-pasvTimeout"><i class="fa fa-clock-o"></i> PASV Timeout</label>
    <input type="text" id="node-config-input-pasvTimeout" placeholder="10000">
  </div>
  <div class="form-row">
    <label for="node-config-input-keepalive"><i class="fa fa-clock-o"></i> Keepalive</label>
    <input type="text" id="node-config-input-keepalive" placeholder="10000">
  </div>
  <div class="form-row">
    <label for="node-input-secure"><i class="fa fa-key"></i> Data connection encryption</label>
    <input type="checkbox" id="node-input-secure" placeholder="once" style="width: 10%; vertical-align: top;">
    <label for="node-config-input-secureOptions" style="width: 20%; vertical-align: top;"> Secure Options</label>
    <input type="text" id="node-config-input-secureOptions" style="width: 35%; vertical-align: top;">
  </div>
</script>

<script type="text/x-red" data-help-name="ftp in">
  <p>Allows LIST, GET, PUT and DELETE of files on a remote FTP server.</p>
  <p><code>msg.filename</code> is the path/name of the file on the remote FTP server</p>
  <p><code>msg.localFilename</code> is the path/name of the file on the local machine</p>
  <p>NOTE: the local machine is where NR is running - this might not be the machine where your browser connects to Node Red.</p>
  <p></p> 
  <p>GET uses <code>msg.filename</code> and <code>msg.localFilename</code></p>
  <p>PUT uses <code>msg.filename</code> and <code>msg.localFilename</code></p>
  <p>DELETE uses only <code>msg.filename</code></p>
  <p>LIST uses only <code>msg.filename</code> and returns an array of the file/folders, containing type, name, sticky, rights(an object), acl, owner, group, size and date.</p>
  <p></p>
  <p>You can pass <code>msg.filename</code> and <code>msg.localFilename</code> to this node, however values entered in the node will take precedence.</p>
  <p></p>
  <p>If an error occurs, the error message will come out as <code>msg</code>.</p>
</script>

<script type="text/javascript">
  RED.nodes.registerType('ftp', {
    category: 'config',
    color:"BurlyWood",
    defaults: {
      host: { value: '' },
      port: { value: '' },
      secure: { value: false },
      secureOptions: { value: '' },
      user: { value: '' },
      connTimeout: { value: '' },
      pasvTimeout: { value: '' },
      keepalive: { value: '' }
    },
    credentials: {
      password: { type: 'password', required: true },
    },
    label: function() {
      return this.host
    }
  });
</script>

<script type="text/x-red" data-template-name="ftp in">
  <div class="form-row">
    <label for="node-input-ftp"><i class="fa fa-user"></i> Add new FTP Server</label>
    <input type="text" id="node-input-ftp">
  </div>
  <div class="form-row">
    <label for="node-input-operation"><i class="fa fa-wrench"></i> Operation</label>
    <select type="text" id="node-input-operation">
      <option value="list">list</option>
      <option value="get">get</option>
      <option value="put">put</option>
      <option value="delete">delete</option>
    </select>
  </div>
  <div class="form-row input-filename-row hidden">
    <label for="node-input-filename"><i class="fa fa-file"></i> Filename</label>
    <input type="text" id="node-input-filename" placeholder="Filename">
  </div>
  <div class="form-row input-localFilename-row hidden">
    <label for="node-input-localFilename"><i class="fa fa-file"></i> Local Filename</label>
    <input type="text" id="node-input-localFilename" placeholder="Local Filename">
  </div>
  <div class="form-row">
    <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
    <input type="text" id="node-input-name" placeholder="Name">
  </div>
</script>

<script type="text/javascript">
  RED.nodes.registerType('ftp in', {
    category: 'storage-input',
    color:"BurlyWood",
    defaults: {
      ftp: { type: 'ftp', required: true },
      operation: { value: 'list', required: true },
      filename: { value: '' },
      localFilename: { value: '' },
      name: { value: '' }
    },
    inputs: 1,
    outputs: 1,
    icon: "file.png",
    label: function () {
      var ftpNode = RED.nodes.node(this.ftp);
      return this.name || 'ftp';
    },
    labelStyle: function () {
      return this.name ? 'node_label_italic' : '';
    },
    oneditprepare: function () {
      var filename = $(".input-filename-row");
      var localFilename = $(".input-localFilename-row");
      $("#node-input-operation").change(function () {
        var id = $("#node-input-operation option:selected").val();
        console.log(id);
        if (id == 'list') {
          filename.show();
          localFilename.hide();
        } else if (id == 'put' || id == 'get') {
          filename.show();
          localFilename.show();
        } else {
          filename.show();
          localFilename.hide();
        }
      });
    }
  });
</script>
  1. and in ftp.js :
module.exports = function (RED) {
  'use strict';
  var ftp = require('ftp');
  var fs = require('fs');

  function FtpNode(n) {
    RED.nodes.createNode(this, n);
    var node = this;
    var credentials = RED.nodes.getCredentials(n.id);
    this.options = {
      'host': n.host || 'localhost',
      'port': n.port || 21,
      'secure': n.secure || false,
      'secureOptions': n.secureOptions,
      'user': n.user || 'anonymous',
      'password': credentials.password || 'anonymous@',
      'connTimeout': n.connTimeout || 10000,
      'pasvTimeout': n.pasvTimeout || 10000,
      'keepalive': n.keepalive || 10000
    };
  }

  RED.nodes.registerType('ftp', FtpNode, {
    credentials: {
      password: { type: 'password' }
    }
  });

  function FtpInNode(n) {
    RED.nodes.createNode(this, n);
    this.ftp = n.ftp;
    this.operation = n.operation;
    this.filename = n.filename;
    this.localFilename = n.localFilename;
    this.ftpConfig = RED.nodes.getNode(this.ftp);
	
    if (this.ftpConfig) {
      var node = this;
      node.on('input', function (msg) {
        var conn = new ftp();
        var filename = node.filename || msg.filename || '';
        var localFilename = node.localFilename || msg.localFilename || '';
        this.sendMsg = function (err, result) {
          if (err) {
            node.error(err, msg);
            node.status({ fill: 'red', shape: 'ring', text: 'failed' });
			node.send(err);
            return;
          }
          node.status({});
          if (node.operation == 'get') {
            result.once('close', function() { conn.end(); });
            result.pipe(fs.createWriteStream(localFilename));
            msg.payload = 'Get operation successful. ' + localFilename;
          } else if (node.operation == 'put') {
            conn.end();
            msg.payload = 'Put operation successful.';
          } else {
            conn.end();
			msg.message = 'Operation successful.';
            msg.payload = result;
          }
          msg.filename = filename;
          msg.localFilename = localFilename;
          node.send(msg);
        };
        conn.on('ready', function () {
          switch (node.operation) {
            case 'list':
              conn.list(filename, node.sendMsg);
              break;
            case 'get':
              conn.get(filename, node.sendMsg);
              break;
            case 'put':
              conn.put(localFilename, filename, node.sendMsg);
              break;
            case 'delete':
              conn.delete(filename, node.sendMsg);
              break;
          }
        });
        conn.on('error', function(err) { 
          node.error(err, msg);
          node.status({ fill: 'red', shape: 'ring', text: err.message });
          node.send(err);
		  return;
        });
        conn.connect(node.ftpConfig.options);
      });
    } else {
      this.error('missing ftp configuration');
    }
  }
  RED.nodes.registerType('ftp in', FtpInNode);
}
  1. Restart Node-Red

image
This flow display files in the folder Test\newFolder

[{"id":"c00bd000.a5f0c","type":"ftp in","z":"a8b1b208.7036f","ftp":"42562df8.ff98b4","operation":"list","filename":"","localFilename":"","name":"list","x":470,"y":1600,"wires":[["a3de95d2.f1f7a8"]]},{"id":"a3de95d2.f1f7a8","type":"debug","z":"a8b1b208.7036f","name":"complet","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":640,"y":1600,"wires":[]},{"id":"b5da47bd.987978","type":"change","z":"a8b1b208.7036f","name":"pay to filename","rules":[{"t":"move","p":"payload","pt":"msg","to":"filename","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":1600,"wires":[["c00bd000.a5f0c"]]},{"id":"19f7caab.4c1865","type":"inject","z":"a8b1b208.7036f","name":"Test\\newFolder","topic":"","payload":"Test\\newFolder","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":1600,"wires":[["b5da47bd.987978"]]},{"id":"42562df8.ff98b4","type":"ftp","z":"","host":"192.168.1.143","port":"21","secureOptions":"","user":"superninja","connTimeout":"","pasvTimeout":"","keepalive":""}]

Let me fork it really quick and embed that patch.

Yes of course

haven't tested anything, just put the patch on it, but feel free to use it. I'm not planning to further support it after offering it as PR to the original package. So switch back to the original node please after the patch gets accepted.
npm install boisei0/node-red-contrib-ftp

1 Like

This topic was automatically closed after 60 days. New replies are no longer allowed.