Nov 23

Seit gestern schlug ich mich mit einem Problem rum, mittels des CPAN-Modules Net::FTP von einem passivem FTP-Server eine Datei zu laden. Leider schlugen alle Versuche immer fehl. Anbei das Beispiel-Script:


#/usr/local/bin/perl

use strict;
use Net::FTP;

# benötigte Variablen
my ($ftpserver, $ftppath, $ftpdatei, $modtime, $update) = ("")x5;
my $ftppassiv = 1;
my $ftpport = 21;
my $ftpuser = "gast";
my $ftppassword = "geheim";
my $ftplink = "ftp.comnart.de/logs/www.comnart.de_ftp_access.log.zip";
my $lokaledatei = "access.log";
my $zipname = "www.comnart.de_ftp_access.log";

# FTP Link zerlegen
if ($ftplink =~ /^(.*?)\/(.*\/)?(.*?)$/) {
$ftpserver = $1;
$ftppath = $2;
$ftpdatei = $3;
} else {
die "Kann den FTP Link nicht zerlegen!\n";
}

# FTP Verbindung aufbauen
my $ftp = Net::FTP->new(
$ftpserver,
PORT => $ftpport,
PASSIVE => $ftppassiv,
Timeout => 60) or die "Konnte die FTP-Verbindung nicht aufbauen: $@\n";

# FTP Login
$ftp->login($ftpuser, $ftppassword) or die "Konnte nicht einloggen";

# Verzeichniss wechseln falls nötig
$ftp->cwd($ftppath) if $ftppath;

# ÜbertragungsModus umschalten, entsprechend der Datei-Endung
## XML, Text, CSV und HTML Dateien im ASCII-Modus
$ftp->ascii() if $ftpdatei =~ /\.(txt|csv|html|xml)$/i;
## Zip, XLS und DBase Dateien im BINÄR-Modus
$ftp->binary() if $ftpdatei =~ /\.(gz|zip|xls|dbf|bz2)$/i;

# FTP-Feature MDTM verfügbar?
if ($ftp->feature( 'MDTM' )) {
$modtime = $ftp->mdtm($ftpdatei)
or print "Konnte das Datum der Datei $ftpdatei: nicht lesen: $@\n";
} else {
# Alternativ Verzeichniss auslesen und Datum extrahieren
my $ls = $ftp->dir();
foreach my $entry (parse_dir($ls)) {
my ($name, $type, $size, $mtime, $mode) = @$entry;
if ($type eq 'f') {
next unless ($name eq $ftpdatei);
$modtime = $mtime;
}
}
}

# Überprüfen, ob die Datei lokal schon aktuell & vorhanden ist
my @lokalinfo = stat($lokaledatei);
if ($modtime ne $lokalinfo[9])
{
# Alte Datei loeschen
unlink $lokaledatei or do print "Fehler beim löschen der alten lokalen Datei! $lokaledatei";

if ($zipname){
my $extraktok = 0;
# Neue Datei per FTP holen
$ftp->get($ftpdatei,$lokaledatei.".zip") or die "Konnte die Datei $ftpdatei -> ".$lokaledatei.".zip nicht empfangen: $@\n";

# Datei entpacken
use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
my $zip = Archive::Zip->new();
die "Winzip 'read error' " if $zip->read($lokaledatei.'.zip') != AZ_OK;
if ($zip->extractMember($zipname,$lokaledatei) != AZ_OK) {
print "Winzip 'write error' ";
} else {
$extraktok = 1;
}

# Zip-Datei loeschen
if ($extraktok) {
unlink $lokaledatei.'.zip' or print "Fehler beim löschen der gezippten Datei!";
} else {
print "Fehler beim Entpacken, Datei nicht gefunden ";
}

} else {
$ftp->get($ftpdatei,$lokaledatei) or do print "Konnte die Datei $ftpdatei: nicht empfangen: $@\n";
$update = 1;
}
} else {
$update = 0;
}

$ftp->close() or print "Konnte Verbindung nicht schließen";

if ($update) {
# Zeitstempel größer aktuelle Zeit?
$modtime = time if ($modtime > time); # Zeit auf Server größer aktueller Zeit
$modtime = time if ($modtime < 1); # Hack: Hier sollte man sich was besseres einfallen lassen ;) # Neue Datei mit Zeitstempel setzen utime ($modtime,$modtime,$lokaledatei); print "Download der Datei erfolgreich! (Timestamp: $modtime)\n"; } else { print "Datei ist aktuell (Timestamp: $modtime)\n"; } exit(0);

Im passiven Modus funktionierte alles bis zum

$ftp->get($ftpdatei,$lokaledatei) or do print "Konnte die Datei $ftpdatei: nicht empfangen: $@\n";

an dieser Stelle brach das Script mit der Meldung, das ein Abruf der Datei nicht möglich sei ab.

Nach langer Suche bei Google fand ich in diesem Forum den passenden Tip! 🙂
Die Lösung ist eigentlich ziemlich simpel. Einfach vor dem Login folgende Zeile einfügen:

$ENV{FTP_PASSIVE} = $passiv ? 1 : undef;

und schon funktioniert es auch mit problematischen passiven FTP-Servern!

Tagged with:
preload preload preload