[リストへもどる]
一括表示
タイトルftpサーバからcsvファイルをダウンロードしたいのですが・・・
記事No8342
投稿日: 2012/02/24(Fri) 10:44
投稿者km
こんにちわ、Perlのプログラムで詰まってしまったので質問させてください。

現在、ローカルのノートPCから、ファーストサーバのftpサーバにアクセスして、csvファイルをダウンロードするプログラムを作成中なのですが、実行すると
> Connect to www.**********.****
> 220 Firstserver FTP Server Ready
> USER
> 500 USER: command requires a parameter
> C:\Users\********\Desktop\ffftp_local\ftpget.pl中断:応答コード500のため

というエラーで止まってしまいます。
ファーストサーバの方に問い合わせてみたのですが、「ローカルで実行するプログラムはサポート対象外です。各種書籍やWEBサイト名などをご参照いただけますでしょうか?」というメールが返ってきただけなので、困っています。

FTPGetのプログラムは 「Perl徹底活用インターネットダイレクトアクセス」という書籍を参考に作りました。
今から自分でも調べてみますが、アドバイスお願いします。

タイトルRe: ftpサーバからcsvファイルをダウンロードしたいのですが・・・
記事No8343
投稿日: 2012/02/25(Sat) 00:07
投稿者瀬戸っぷ   <setoppu@gmail.com>
> > USER
> > 500 USER: command requires a parameter
> というエラーで止まってしまいます。

ログインするユーザ名が指定されていなければ、サーバだって困ると思いますが…

> FTPGetのプログラムは 「Perl徹底活用インターネットダイレクトアクセス」という書籍を参考に作りました。

自作…なんですか?
プログラムが間違っている。
ということは考えられないんですか?
# コード無いからわかりませんが…。

タイトルRe^2: ftpサーバからcsvファイルをダウンロードしたいのですが・・・
記事No8344
投稿日: 2012/02/25(Sat) 10:08
投稿者km
すみません、ソースをコピペします。

#!/usr/bin/perl --

# ftpget.pl - FTPクライアント(GET)
# 2012/02/20 作成
# perl ftpget.cgi FTPサーバー ユーザー名 パスワード パス
# タイプはバイナリーに固定

# 以下の変数を使う為、Socket定義、TCP定義をしなければならない
use Socket;
use TCP;

# コマンドラインの引数 shiftを使って手入力するのが本来のソースだが、
# 2012/2/21 仕様変更 最初から入力済みにする
$HostName = '*********************';
$Usre = '**********';
$Pass = **************;
#$Path = '*******************************';
$Path = '***********************';

# パスをディレクトリとファイル名に分ける
$Path =~ /[^\\\/]+$/;
$File = $&;
($Dir = $') =~ s/[\\\/]$//;
print "$Path\n";
print "$File\n";
print "$Dir\n";

# 保存するファイルをオープン
open(FILE,">$File") or die("$Fileがオープンできません");
binmode(FILE);

# 指定されたホストのFTPポートに接続する
TcpConnect(ControlSock,$HostName,'ftp') or die("$TCP::Error:$!\n");
# ソケットへの出力はバッファリングしない
select(ControlSock); $| = 1; select(STDOUT);
print "Connect to $HostName\n";

# ローカルアドレスを得る
($MyPort,$MyAddr) = sockaddr_in(getsockname(ControlSock));
# PORTコマンド用文字列にする
$PortStr = sprintf("%d, %d, %d, %d, %d, %d", unpack("C4",$MyAddr),
unpack("C2",pack("n",$MyPort)));
# listenするソケットをオープン
# SO_REUSEADDRオプションは同じローカルアドレスで複数接続できるようにする
TcpListen(ListenSock,$Myport,$MyAddr,5,SO_REUSEADDR) or die("$TCP::Error : $!\n");

$error = 0;
USESOCK:
{
&ReceiveCode == 220 or $error = 1,last;
&SendString("USER $User\n");
&ReceiveCode == 331 or $error = 1,last;
&SendString("PASS $Pass\n");
&ReceiveCode == 230 or $error = 1,last;
if ( $Dir ne '' )
{
&SendString("CWD $Dir\n");
&ReceiveCode == 250 or $error = 1,last;
}
&SendString("TYPE I\n");
&ReceiveCode == 200 or $error = 1,last;
&SendString("PORT $PortStr\n");
&ReceiveCode == 200 or $error = 1,last;
&SendString("RETR $File\n");
&ReceiveCode == 150 or $error = 1,last;

# サーバーからデータ接続を受ける
$sockaddr = accept(DataSock, ListenSock);
# listenソケットはもう不要なので閉じる
close(ListenSock);

# リモートのアドレスからポート番号、IPアドレス、ホスト名を得る
{
my($port,$ipaddr) = sockaddr_in($sockaddr);
my $name = gethostbyaddr($ipaddr,AF_INET);
$ipaddr = inet_ntoa($ipaddr);
print "$name($ipaddr)のポート番号$portから接続\n";
}

$writeerror = 0;
print "$Fileの転送開始\n";
while( read(DataSock, $buf, 1000) )
{
print FILE $buf or $writeerror = 1,last;
}
close(FILE);
close(DataSock);
if( $writeerror )
{ print "$Fileの書き込みエラーで転送中断\n"; }
else
{ print "$Fileの転送終了\n"; }

&ReceiveCode == 226 or $error = 1,last;
# 終了
&SendString("QUIT\n");
&ReceiveCode == 221 or $error = 1,last;
}
# end USESOCK:
if ( $error )
{ print STDERR "$0中断:応答コード$ReceiveCodeのため\n";}
close(controlSock);
exit;
# end

# 文字列を送信、標準出力にも
sub SendString
{
local($_) = @_;

print;
# 行末はCR+CFにして送る

s/([^\r])\n/$1\r\n/g;
print ControlSock;
}
# end SendString

# 応答コードを得る
sub ReceiveCode
{
my $line;

$ReceiveCode = 0;
while( $line = <ControlSock> )
{
print $line;
# 応答コード+スペース で始まるならそれで応答は終わり
if( $line =~ /^(\d+) / )
{ $ReceiveCode = $1; last; }
}
$ReceiveCode;
}
# end ReciveCode

# end ftpget.pl

タイトルRe^3: ftpサーバからcsvファイルをダウンロードしたいのですが・・・
記事No8345
投稿日: 2012/02/25(Sat) 14:21
投稿者km
やはり、ラクダ本という本を手に入れたほうがいいのでしょうか・・・・載ってるかな?

タイトルタイポ?
記事No8346
投稿日: 2012/02/25(Sat) 17:12
投稿者おやじ
全体を見る気はありませんが、事象からすると一番疑われるのがタイポですが、案の定・・・。

> $HostName = '*********************';
> $Usre = '**********';
 ^^^^^^

 (snip)

> USESOCK:
> {
>       &ReceiveCode == 220 or $error = 1,last;
>       &SendString("USER $User\n");
                          ^^^^^^ 

因みにおやじは趣味の領域なのでネットだけで勉強しているため、ソフトはある程度組めるようになりましたが、プロが見たら笑っちゃうレベルで汚いです。

タイトルRe: タイポ?
記事No8348
投稿日: 2012/02/27(Mon) 10:14
投稿者km
コメントありがとうございます!!
スペルが違っていたとは;;
スペルミスでもある程度動くんですね・・・・

> 全体を見る気はありませんが、事象からすると一番疑われるのがタイポですが、案の定・・・。
>
> > $HostName = '*********************';
> > $Usre = '**********';
>  ^^^^^^
>
> (snip)
>
> > USESOCK:
> > {
> > &ReceiveCode == 220 or $error = 1,last;
> > &SendString("USER $User\n");
> ^^^^^^
>
> 因みにおやじは趣味の領域なのでネットだけで勉強しているため、ソフトはある程度組めるようになりましたが、プロが見たら笑っちゃうレベルで汚いです。

タイトルRe^2: タイポ?
記事No8349
投稿日: 2012/02/27(Mon) 11:24
投稿者km
何度もすみません・・・・kmです・・・
おやじさんにご指摘いただいた箇所を修正して動かしたのですが、
別のエラーが出て止まりました。

応答コード0という謎のエラーが出ました。
エラー原因のコードを調べたのですが、コード0の応答コードは無かったです。

Connect to ***************
220 ************ FTP Server Ready
USER *******
331 Password required for *******
PASS *********
230 User ****** logged in
TYPE I
200 Type set to I
PORT ***, ***, *, **, ***, **
C:\ffftp_local\ftpget.pl中断:応答コード0のため

タイトルRe^3: タイポ?
記事No8350
投稿日: 2012/02/27(Mon) 16:31
投稿者km
すみません、よく見るとまだタイポみたいです・・・
頑張ります・・・・ぅぅ

タイトルRe^4: タイポ?
記事No8351
投稿日: 2012/02/27(Mon) 16:39
投稿者km
何度もすみません、タイポ解決してもダメでした。
という報告だけさせてください。

タイトルNet::FTPを使ったらどうですか?
記事No8352
投稿日: 2012/02/27(Mon) 20:21
投稿者おやじ
> Connect to ***************
> 220 ************ FTP Server Ready
> USER *******
> 331 Password required for *******
> PASS *********
> 230 User ****** logged in
> TYPE I
> 200 Type set to I
> PORT ***, ***, *, **, ***, **
> C:\ffftp_local\ftpget.pl中断:応答コード0のため

現状のエラーは、単にPORTコマンド用文字列の作り方がおかしいからです。
カンマ区切りでアドレスとポートを書きますが、空白を入れては駄目です。

誤:$PortStr = sprintf("%d, %d, %d, %d, %d, %d", unpack("C4",$MyAddr),unpack("C2",pack("n",$MyPort)));
正:$PortStr = sprintf("%d,%d,%d,%d,%d,%d", unpack("C4",$MyAddr),unpack("C2",pack("n",$MyPort)));

それ以外も直さないと動かないと思いますが、これは自分で書いたのですか?
本来の目的は何なのでしょうか?
perlの勉強? ソケット通信の勉強? FTP通信の勉強?
単にFTPでCSVファイルがダウンロードできれば良いだけなら何もこんな難しいことしなくても良いのでは?
perlはいろいろなモジュールがあるので、それらを使えば簡単にできます。
例えば、今回のケースならNet::FTPモジュールです。以下で、ほぼ同じことができます。
下記の例でのFTPのモードはPassiveモードですが、PORTモードではないとまずい(元のソフトはこちらですが、こちらを使う意味がよくわかりません。)場合は、
「$ftp = Net::FTP->new($HostName, Passive => 1)」のところの「Passive => 1」を「Passive => 0」としてください。


----- ここから -----
#!/usr/bin/perl --

use warnings;
use Net::FTP;

$HostName = '***.***.***';
$User = '********';
$Pass = '********';
$Path = '/aaaaa/bbbbb/ccccc.csv';

# パスをディレクトリとファイル名に分ける
$Path =~ m|[^\\\/]+$|i;
$Dir = $`;
$File = $&;

print "Path:$Path\n";
print "File:$File\n";
print "Dir :$Dir\n";

$ftp = Net::FTP->new($HostName, Passive => 1)
|| die "Cannot connect to '$HostName': $!";

$ftp->login($User,$Pass)
|| die "Cannot login '$HostName:$User':" . $ftp->message;

if ( $Dir ne '' )
{
$ftp->cwd($Dir)
|| die "FTP command fail: " . $ftp->message;
}

$ftp->get($File)
|| die "FTP command fail: " . $ftp->message;

$ftp->quit;
----- ここまで -----

タイトルRe: Net::FTPを使ったらどうですか?
記事No8354
投稿日: 2012/02/28(Tue) 10:08
投稿者km
返信ありがとうございます。
Net::FTPを使わなかったのは、Perlの学習本には、Net::FTPを使う方法が乗っていなかった為です。

学習本には中途半端なものしか乗っていなかったようです・・・

Net::FTPを使ってやってみます。

タイトルRe^2: Net::FTPを使ったらどうですか?
記事No8355
投稿日: 2012/02/28(Tue) 11:01
投稿者km
kmです。
ダウンロード成功しました。

本当にありがとうございました。