使用Net::FTP进行巡游

当我们想要在机器之间交换文件时,我们通常会想到rsync、scp、git,甚至是缓慢且复杂的(看着你Artifactory和S3),但答案通常就在你的眼前:FTP!
“文件传输协议”提供了一种非常简单方便的文件共享方式。它经受了考验,几乎不需要维护,并且具有简单的匿名访问机制。它可以与多种标准认证方法以及一些虚拟认证方法集成,这里我没有展示。
在这篇文章中,我将安装一个本地FTP服务器,并在Perl中创建一个简单的FTP客户端。
一些背景信息
在《$work》中,我必须维护一支由开发者组成的队伍,他们从手工制作的本地配置文件中创建定制的构建管道。
这个文件并非“有意”托管,就像你在Travis CI或GitHub Action中看到的那样,但它被用来向一个“重型客户端”提供数据,该客户端通过HTTP API调用解析、解析模板,并在一些集中的自动化服务器中创建工作空间。
帮助开发者根据规范创建这个文件需要大量的支持(又一个文件格式),当我们想要帮助他们解决失败的工作空间创建/构建问题时,我们是无助的(无法从工作空间中检索配置)。
我得到了一个想法,在创建构建管道工作空间时自动备份和集中配置文件。这旨在帮助开发者(配置“示例”)和支持团队(查看历史记录,版本化后我们可以检查差异,文件可回放)。约束条件是能够从各种地方与不同的用户交换文件。FTP协议非常适合这个。
我还添加了一个cron作业来自动提交和推送到git仓库,我们神奇地拥有了一个列出版本化配置文件的网站。
此外,FTP后来证明也几乎不需要支持。我的意思是,真的不需要维护!
下载和安装ftpd
我决定使用pure-ftpd,但如果你想要,还有一些其他的良好替代方案。
首先,我下载了tar包,解压它,然后进入其目录
$ wget https://download.pureftpd.org/pub/pure-ftpd/releases/pure-ftpd-1.0.49.tar.gz
$ tar xvzf pure-ftpd-1.0.49.tar.gz
$ cd pure-ftpd-1.0.49/
我配置了ftpd
,以便我可以作为一个普通用户(非root用户)使用非限制端口执行它,并将目标目录设置为我的$HOME/ftpd
$ ./configure --prefix=$HOME/ftpd --with-nonroot && make && make install
我创建了两个目录。ftp是我将要发布和运行的目录,我将在这里放置pidfile。
$ cd $HOME/ftpd
$ mkdir ftp
$ mkdir run
现在我们可以启动FTP服务器了。我需要提供一些自定义配置
FTP_ANON_DIR
是我想要发布的目录-e
允许匿名访问-M
允许匿名用户创建目录-g
指定pidfile的目录$ FTP_ANON_DIR=`pwd`/ftp ; ./sbin/pure-ftpd -e -M -g run &
此时,我应该有一个正在运行的FTP服务器。让我们检查一下!
使用ftp进行测试
首先,我用预安装的ftp
客户端进行测试。如果一切正常,我会看到典型的FTP交换
$ ftp localhost 2121
Connected to localhost.
220---------- Welcome to Pure-FTPd ----------
220-You are user number 1 of 50 allowed.
220-Local time is now 11:56. Server port: 2121.
220-Only anonymous FTP is allowed here
220 You will be disconnected after 15 minutes of inactivity.
Name (localhost:tib):
230 Anonymous user logged in
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
如果我得到ftp: connect: Connection refused
,可能是因为以下原因之一:
ftpd
没有运行(使用ps aux | grep "ftp[d]"
进行检查)- 我使用了错误的端口
如果我得到421 Can't change directory to /home/tib/ftpd/ftp/ [/]
,可能是因为我没有创建在FTP_ANON_DIR
中指定的目录。
Perl中的简单客户端
好的,这很酷,但到目前为止我只玩过ftp服务器和预安装的ftp
客户端。现在写一些Perl怎么样?
Net::FTP是一个针对FTP协议的出色的CPAN模块,我会使用它。
简单列表
首先,一个非常简单的列表脚本ls.pl
。这个程序连接到服务器,请求文件列表,并输出每个文件。很明显,用Perl玩FTP非常简单直接!
#!/usr/bin/env perl
use warnings;
use strict;
use Net::FTP;
my $HOST = "localhost";
my $PORT = 2121;
my $ftp = Net::FTP->new($HOST, Port => $PORT, Debug => 0)
or die "Cannot connect to $HOST: $@";
$ftp->login() or die "Cannot login ", $ftp->message;
foreach my $f ($ftp->ls()) { print "$f\n"; }
$ftp->quit;
上传
接下来呢?也许上传一些东西?同样,这很简单。不是列出文件,而是put
上传它们
#!/usr/bin/env perl
use warnings;
use strict;
use Net::FTP;
my $HOST = "localhost";
my $PORT = 2121;
my $ftp = Net::FTP->new($HOST, Port => $PORT, Debug => 0)
or die "Cannot connect to $HOST: $@";
$ftp->login() or die "Cannot login ", $ftp->message;
foreach my $file(@ARGV) {
$ftp->put("$file", "$file")
or die "Cannot put $file", $ftp->message;
}
$ftp->quit;
我运行这个脚本,并提供了我想上传的文件
$ perl upload.pl file1.txt file2.txt`.
整合起来
我提出一个更完整的客户端,具有一些命令行解析和更多操作。除了之前的列表和上传代码,这里我添加了查看文件的方式。使用Getopt::Long来处理命令行参数。
#!/usr/bin/env perl
use warnings;
use strict;
use Getopt::Long;
use File::Slurp;
use Net::FTP;
my $HOST = "localhost";
my $PORT = 2121;
my %options = ();
GetOptions(
"action|c=s" => \$options{'action'},
"file|f=s" => \$options{'file'},
"help|h" => \$options{'help'}
);
sub print_usage() {
print "List all files :\n\t$0 -c list\n";
print "Upload file :\n\t$0 -c upload -f file.txt\n";
print "Print file :\n\t$0 -c view -f file.txt\n\n";
}
sub get_ftp() {
my $ftp = Net::FTP->new($HOST, Port => $PORT, Debug => 0)
or die "Cannot connect to $HOST: $@";
$ftp->login() or die "Cannot login ", $ftp->message;
}
# ls / on remote ftp
sub list() {
my $ftp = get_ftp();
foreach my $f ($ftp->ls()) { print "$f\n"; }
$ftp->quit;
}
# Upload a file
sub upload($) {
my $file = shift;
(-e $file) or return 1;
my $ftp = get_ftp();
$ftp->login() or die "Cannot login ", $ftp->message;
$ftp->put("$file") or die "Cannot put $file ", $ftp->message;
$ftp->quit;
}
# Read a file
sub view($) {
my $file = shift;
my $ftp = get_ftp();
$ftp->get("$file") or die "Cannot read $file ", $ftp->message;
if(-e $file) { print read_file($file); }
$ftp->quit;
}
if($options{'action'} eq 'list') {
list();
} elsif($options{'action'} eq 'upload') {
upload($options{'file'});
} elsif($options{'action'} eq 'view') {
view($options{'file'});
} else {
print_usage();
}
更多关于设计和安全
这个薄薄的包装可以扩展以执行更多任务,例如检查允许或不允许的名称模式,或者根据上传者或文件名称的前缀整理文件。记住,这只是在客户端!如果你想得到真正的保障,你最好在服务器端也实施一些保护措施。但是,目标不是在这里讨论安全,而是要玩FTP!我希望你已经和我一起愉快地游览了Net::FTP!
标签
反馈
这篇文章有什么问题吗?请在GitHub上打开一个问题或pull request来帮助我们。