Using Callbacks with Net::SFTP::Foreign in Perl

Overview

One of the more robust SFTP modules for Perl is Net::SFTP::Foreign.  Among its many features, it has a find() method that performs a recursive search over a path and returns a list.  It also accepts a callback subroutine as an argument which allows us to customize the search filter.   

find() method

The find method relies on the callback to determine if we want the file in our list.   The callback should return 1 for a files accept and 0 for files we reject.

    Code

    Here we are retrieving a list of files that meet the following criteria:

    • File timestamp is from yesterday
    • File size is under 2GB
    • File name confirms to a standard (/sftp/vendor/output_*.mp3 or wav)
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use Net::SFTP::Foreign;
    use Fcntl qw(S_ISDIR S_ISREG);
    use DateTime::Format::Strptime;
    
    # Get start epoch time - yesterday at midnight
    my $dt = DateTime->now(time_zone => 'America/New_York');
    $dt->subtract(days => 1);
    $dt->set_hour(0);
    $dt->set_minute(0);
    $dt->set_second(0);
    
    my $epoch_start = $dt->epoch;
    
    # Get end epoch time - yesterday at 23:59:59
    $dt->set_hour(23);
    $dt->set_minute(59);
    $dt->set_second(59);
    
    my $epoch_end = $dt->epoch;
    
    my $sftp_host = 'sftp.vendor.com';
    my $sftp_username = 'username';
    my $sftp_password = 'xxxxx';
    my $sftp_port = 22;
    
    # Max file size
    my $max_size = 2147483648;
    
    my $path = '/sftp/vendor';
    
    my $sftp = Net::SFTP::Foreign->new($sftp_host,port => $sftp_port, user => $sftp_username, password => $sftp_password, stderr_discard => 1);
    
    # Retrieve list of files matching criteria
    my @list = $sftp->find($path, names_only => 1, wanted => \&callback);
    
    $sftp->disconnect;
    
    # Perform validation checks
    sub callback
    {
        my($sftp, $data, $offset, $size) = @_;
    
        my $check_type          = ( S_ISREG($data->{a}->perm) );
        my $check_size          = ( $data->{a}->size < $max_size );
        my $check_timestamp     = ( $data->{a}->mtime >= $epoch_start &&  $data->{a}->mtime <= $epoch_end );
        my $check_name          = ( $data->{filename} =~ m[^$path/output_*\.(mp3|wav)]i );
    
        # Return true if all checks pass
        return 1 if ($check_name && $check_type && $check_size && $check_timestamp);
    
        # Otherwise, return false
        return 0;
    }