http://www.perlmonks.org?node_id=166889

arajani has asked for the wisdom of the Perl Monks concerning the following question:

Hi All Monks,

I am ftp'ing multiple files to another system. For this I
am using Net::FTP. The code is below. I am wondering how
to capture the return value of the ftp. Success or failure.

I am interested in the return values of the following.

1. when FTP->new() call was invoked
2. when all the files were ftp'd. i.e., at FTP->quit
   Even if one of the file is not ftp'd I want to make it
   a failure. Or do I need to check the status for each of
   the file transfered?
   Further to this, is there a way to ftp all the files at
   one shot .. by using equivalent of mput ??

Is it possible? pls. let me know.
$ftp = Net::FTP->new("mysys",Port => 9021,Timeout => 20, Debug => 0); $ftp->login("anonymous",'me@myself.com'); $ftp->put("$dir/$file1"); $ftp->put("$dir/$file2"); $ftp->put("$dir/$file3"); $ftp->quit;
Thanks in advance,

Raj.

Edit kudra, 2002-05-16 Closed open PRE tag

Replies are listed 'Best First'.
Re: ftp return value
by derby (Abbot) on May 16, 2002 at 02:22 UTC
    Raj, Check out the Net::FTP documentation. Most ftp methods return undef on failure. So check after each operation. Wraping in eval will also help.

    eval { $ftp = Net::FTP->new("mysys",Port => 9021, Timeout => 20, Debug => 0) or die "Cannot create ftp object"; $ftp->login("anonymous",'me@myself.com') or die "Cannot login"; $ftp->put("$dir/$file1") or die "Cannot put"; $ftp->put("$dir/$file2") or die "Cannot put"; $ftp->put("$dir/$file3") or die "Cannot put"; $ftp->quit; } if( $@ ) { print "We had an issue: ", $@, "\n"; } else { print "A-Okay"; }

    I don't see an mput type method, but it should be real trivial to code:

    @files = qw( $file1 $file2 $file3 ); foreach( @files ) { $ftp->put( $_ ) or die "Cannot put"; }

    -derby

Re: ftp return value
by andreychek (Parson) on May 16, 2002 at 02:44 UTC
    Hello,

    Yeah, the Net::FTP docs seem kind of lacking on this information. While I haven't tried the following answers out for myself, I got them from a program which works, so I'd assume they are correct:

    1. when FTP->new() call was invoked

    Well, this is just a typical constructor, the FTP code hasn't tried to do anything with a remote system yet. However, you can check for the return value of the login() call, like so:
    my $max_tries = 5; my $tries = 0; # login() returns false, if unsuccessful while( ! $ftp->login('anonymous', 'me@myself.com' ) { $tries++; die "We tried $tries times, but cannot connect!\n" if $max_tries == +$tries; warn "Attempt $tries: Could not connect, trying again.\n"; } print "Yay! We logged in!\n";

    2. when all the files were ftp'd. i.e., at FTP->quit

    The return value of $ftp->quit would actually tell you if the quit() call was successful or not, which isn't what I think you're worried about.

    What you'd need to do is check the call of each put() method:
    $ftp->put("$dir/$file1") or die "FTP did not succesfully upload $dir/ +$file!";

    Or, if you don't actually want to die, you could also just set a flag if the put fails. Just like login(), put returns a true value if successful, false otherwise.

    Hope that helps!
    -Eric

    Update: Yeah, what derby said, he managed to type it in quicker then myself, and I like his eval trick :-)

    --
    Lucy: "What happens if you practice the piano for 20 years and then end up not being rich and famous?"
    Schroeder: "The joy is in the playing."
Re: ftp return value
by grinder (Bishop) on May 16, 2002 at 10:10 UTC
    The mput command is not implemented in Net::FTP, even though the documentation doesn't explicitly state the fact.

    You can check $! for errors after a put, but from experience, this only relates to problems on the local host (e.g. "file not found"). To be absolutely certain that the file was tranferred to the remote host you could always do a dir and check the size of the remote file (modulo different sizes if transferring in ASCII between Unix and MS-DOS etc.).

    You could gain a bit of clarity (and, possibly, encapsulate stuff) by using a loop:

    for my $file ( $file1, $file2, $file ) { my $f = "$dir/$file"; $ftp->put( $f ) or warn "could not put $f: $!\n"; my $remote_size = get_remote_size( $f ); my $local_size = -s $f; if( $remote_size != $local_size ) { warn "$f not transferred correctly (local=$local_size, remote=$rem +ote_size)\n"; } }

    ... where the routine get_remote_size is left as an exercise to the reader. Hmmm... I should have read giant's post more carefully. The code presented there appears to do just that.


    print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u'
Re: ftp return value
by kodo (Hermit) on May 16, 2002 at 06:53 UTC
    lo Raj, I've had similar problems and I've finally done it this way:

    use Net::FTP; use Net::Ping; # Calling the subs...: # Checking if host is up... sub pingcheck { my $host = shift; if ($ping->ping($host)) { return 0; } else { print "$host is dead...\n"; return 1; } } # Connect to Server... sub connectftp { my $host = $_[0]; unless ($ftp = Net::FTP->new($host, Timeout => 6, Debug => 0)) { print "Couldn't connect to $host\n"; return 1; } return 0; } # Do login... sub loginftp { my ($user, $pw) = @_; unless ($ftp->login($user, $pw)) { print "Couldn't login\n"; return 1; } return 0; } # Set transfer-mode sub modeftp { my $mode = shift; if ($mode eq 'B') { $mode = "binary"; } elsif ($mode eq 'A') { $mode = "ascii"; } elsif ($mode eq 'E') { $mode = "ebcdic"; } elsif ($mode eq "Y") { $mode = "byte"; } else { $mode = "binary"; } unless ($ftp->$mode()) { print "Couldn't set transfermode to $mode!\n"; return 1; } print "$mode\n"; return 0; } # Now here we have the GET-Part, it supports *.txt also (mget), and yo +u can even check if the sizes of remote and local-file are equal (onl +y makes sense with binary data often) sub getftp { my ($file, $localfile) = @_; my ($remotesize, $localsize) = ''; my @files = (); if (! $localfile) { $file =~ /.*\/(.*)$/; $localfile .= $1; } if ($localfile =~ /\/$/) { $file =~ /.*\/(.*)$/; $localfile .= $1; + } if ($file =~ /\*/) { unless (@files = $ftp->ls($file)) { print "Couldn't download $file as $localfile: $file does n +ot exist?!\n"; return 2; } } else { push(@files, $file); } foreach $file (@files) { if (! $localfile) { ($localfile) = $file =~ /.*\/(.*)$/; } unless (@_ = $ftp->ls($file)) { print "Couldn't download $file as $localfile: $file does n +ot exist?!\n"; return 2; } unless ($ftp->get($file, $localfile)) { print "Couldn't download $file as $localfile!\n"; return 1; } if ($checksize == 1) { $localsize = ((stat($localfile))[7]); foreach($ftp->dir($file)) { ($remotesize) = $_ =~ /^.*?\s+ +.*?\s+.*?\s+.*?\s+(.*?)\s.*?/; } if ($localsize ne $remotesize) { print "Failure while downloading $file as $localfile: +Sizecheck failed ($localsize != $remotesize)!\n"; return 1; } } print "Downloaded $file\n"; } return 0; }


    I hope this helps you a bit, the code can for sure be optimized but it's a pretty good way I think to check if everything went right...

    Greets,

    giant_

    -----BEGIN PERL GEEK CODE BLOCK----- Version: 0.01 P++>+++$c-> P6 >+R+>+M+>++O >+MA+>+++E+>++PU+>+++BD C+>++D!S X!WP >+++MO?PP++n CO?PO-o+G A--OL!Ee---Ev++Eon!Eot!Eob!Eoa!uL++uB uS!uH+uo+w---m!osA-osBE- ------END PERL GEEK CODE BLOCK------
Re: ftp return value
by atopolc (Pilgrim) on May 16, 2002 at 12:51 UTC
    To do proper error checking after each put. I think you should check the return code explicitly and then return the error message from ftp. Here is how I would do it.
    $ftp = Net::FTP->new("mysys",Port => 9021,Timeout => 20, Debug => 0); $ftp->login("anonymous",'me@myself.com'); $ftp->put("$dir/$file1"); my $rtncode=$ftp->code; if ($rtncode != 226 && $rtncode != 250) { my $msg=$ftp->message; print "FTP Failed: $msg\n"; $ftp->quit; } #do remainder of your puts here.

    The '226' and the '250' are both return codes used in FTP. You can read more about them in the RFC for ftp.
    update: to fix link