PHPでPASV(passiveモード)なFTP

しようと思ったらあれもだめこれもだめと言われたので
cURL、君に決めた!

ftp関数が使えない

マニュアルのサンプルどおり書いて終了、と思ったら・・・

Fatal error: Call to undefined function ftp_connect()

マニュアル:

PHPFTP 関数を使用するには、PHP をインストールする際に --enable-ftp オプションを追加する必要があります。

うへっ。

じゃぁftpラッパ使うわ → アウツ

マニュアル:

ただ単にFTPサーバ上のファイルを読み書きしたいだけなら、ファイルシステム関数でftp://ラッパを用いることを考えてください。
こちらのほうがシンプルで直感的なインターフェースを提供します。

シンプルで直感的、ナイス。

がしかし、こちらもうまくいかない。

Warning: fopen(): connect() failed: Connection timed out

Warning: fopen(ftp://...@hoge.example.com/dir/file.jpg): failed to open stream: FTP server reports STOR

サーバーのほうに0byteのファイルだけはできるがその後しばらく固まって↑のエラーになる。
php.netだとNAT関連とか言っている人がいたけど原因不明。
http://jp.php.net/manual/ja/function.fopen.php#39448

fsock_open → めんどい ><

結局やめたけど↓がとても参考になった
http://engineer.ns-it.net/php_47.php
http://engineer.ns-it.net/php_49.php

cURL、君に決めた!

$remote_file = 'ftp://user:passwd@hoge.example.com/dir/file.jpg';
$local_file = 'file.jpg';

$fp = fopen($local_file);
$ch = curl_init();
curl_setopt($ch, CURLOPT_FTP_USE_EPSV, false);
curl_setopt($ch, CURLOPT_UPLOAD, true);
curl_setopt($ch, CURLOPT_URL, $remote_file);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($local_file));
curl_exec($ch);
curl_close($ch);

↓のオプションがミソで、
curl_setopt($ch, CURLOPT_FTP_USE_EPSV, false);

TRUE を設定すると、FTP 転送の際にまずEPSVコマンドの利用を試みます。
失敗した場合はPASVを利用します。FALSE を設定すると、EPSV を無効にします。

これがデフォルトでtrueらしく、そのままだとactiveモードで接続しようとしているのか、かなり長い時間待たされる。