上周给某单位做了一次开发培训, 学员们希望学习用FTP上传下载的Java实现. 上网找了下, 最后找到了两个, 一个是 Apache的Jakarta Commons Net来实现, 其设置选项还是挺多的, 网上的资料也很多, Google一下一大把, 另一个则是http://www.enterprisedt.com/ 开发的一个开源和商业版本的FTP类库, 商业版本支持批量目录的上传和下载. 用的过程中发现了中文问题, 不过最后还是胜利解决了.

    先说一下搭建测试FTP服务器, 一般Windows下用的多的有Server-U(收费)等, 开源的有FileZilla FTP Server(经测试貌似无法上传超过100MB的文件, 不知道哪里有设置, 最后否定了), 目前使用的是一款免费绿色小巧的FTP服务器来做测试: TYPSoft FTP Server. 下载后直接解压缩即可运行, 不过如果要显示中文界面的话, 请修改config.ini:

LangFile=chineses

之后建立用户进行测试就可以了. 界面如下所示:

image

 

一般的客户端连接中文Windows下的FTP服务器, 默认编码是GB2312, 因此不加设置的话很容易无法上传和下载中文附件. 网上有一些代码片段讨论Jakarta Commons Net, 但是看起来正确的解决此问题的代码不多. 其实FtpClient类已经提供了设置的方法, 调用:

ftpClient.setControlEncoding(“gb2312”);

即可, 这样在打开Socket的时候都会才用正确的reader和writer了. 相关的源码片段如下:

/**
 * Sets the character encoding used by the FTP control connection.
 * Some FTP servers require that commands be issued in a non-ASCII
 * encoding like UTF-8 so that filenames with multi-byte character
 * representations (e.g, Big 8) can be specified.
 *
 * @param encoding The new character encoding for the control connection.
 */
public void setControlEncoding(String encoding) {
    _controlEncoding = encoding;
}


/**
 * @return The character encoding used to communicate over the
 * control connection.
 */
public String getControlEncoding() {
    return _controlEncoding;
}

 

下面要说的是edtftpj, 去其官方网站下载得到ZIP, 解压缩后即可运行其自带的例子, 不过默认清空下不支持汉字. 例子及压缩包内容如下图所示:

image

可见支持的功能还是挺全面的, 要看的例子就是upload_download_and_delete_a_file, 现在新建一个Java项目, 把libedtftpj.jar加入项目即可,然后将例子复制进来编译运行, 如下所示:

import com.enterprisedt.net.ftp.FileTransferClient;
import com.enterprisedt.util.debug.Level;
import com.enterprisedt.util.debug.Logger;
import java.io.File;

public class UploadDownloadFiles {

    public static void main(String[] args) {

        // we want remote host, user name and password
        if (args.length < 3) {
            System.out
                    .println("Usage: run remote-host username password");
            System.exit(1);
        }

        // extract command-line arguments
        String host = args[0];
        String username = args[1];
        String password = args[2];
        String filename = "UploadDownloadFiles.java";

        // set up logger so that we get some output
        Logger log = Logger.getLogger(UploadDownloadFiles.class);
        Logger.setLevel(Level.INFO);

        FileTransferClient ftp = null;

        try {
            // create client
            log.info("Creating FTP client");
            ftp = new FileTransferClient();

            // set remote host
            ftp.setRemoteHost(host);
            ftp.setUserName(username);
            ftp.setPassword(password);

            // connect to the server
            log.info("Connecting to server " + host);
            ftp.connect();
            log.info("Connected and logged in to server " + host);

            log.info("Uploading file");
            ftp.uploadFile(filename, filename);
            log.info("File uploaded");

            log.info("Downloading file");
            ftp.downloadFile(filename + ".copy", filename);
            log.info("File downloaded");

            log.info("Deleting remote file");
            ftp.deleteFile(filename);
            log.info("Deleted remote file");

            File file = new File(filename + ".copy");
            file.delete();
            log.info("Deleted local file copy");

            // Shut down client
            log.info("Quitting client");
            ftp.disconnect();

            log.info("Example complete");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
具体操作包括上传,下载,不过当文件名为中文时候, 上传和下载都会出现问题, 报错提示服务器找不到文件. 最终解决方法是继承FileTransferClient,然后获取连接时的配置信息然后修改交互时的字符集:
import com.enterprisedt.net.ftp.FileTransferClient;

/**
 * 可以设置连接时的字符集的FTP客户端.
 * @author BeanSoft
 * 2008-11
 */
public class SetEncodingFileTransferClient extends FileTransferClient {
    /**
     * 设置连接时的字符集, 默认值是US-ASCII.
     * @param controlEncoding 字符集名, 如GB2312等
     */
    public synchronized void setControlEncoding(String controlEncoding) {
        super.masterContext.setControlEncoding(controlEncoding);
   }
}

相应的测试代码是:

import java.io.File;

import com.enterprisedt.util.debug.Level;
import com.enterprisedt.util.debug.Logger;

public class UploadDownloadFiles {

    public static void main(String[] args) {
        // extract command-line arguments
        String host = "localhost";
        String username = "test";
        String password = "test";
        String filename = "图片输出.gif";

        // set up logger so that we get some output
        Logger log = Logger.getLogger(UploadDownloadFiles.class);
        Logger.setLevel(Level.INFO);

        SetEncodingFileTransferClient ftp = null;

        try {
            // create client
            log.info("Creating FTP client");
            ftp = new SetEncodingFileTransferClient();

            // set remote host
            ftp.setRemoteHost(host);
            ftp.setUserName(username);
            ftp.setPassword(password);
            ftp.setControlEncoding("GB2312");

            // connect to the server
            log.info("Connecting to server " + host);
            ftp.connect();
            log.info("Connected and logged in to server " + host);

            log.info("Uploading file");
            ftp.uploadFile(filename, filename);
            log.info("File uploaded");

            log.info("Downloading file");
            ftp.downloadFile(filename + ".copy", filename);
            log.info("File downloaded");

            log.info("Deleting remote file");
            //ftp.deleteFile(filename);
            log.info("Deleted remote file");

            File file = new File(filename + ".copy");
           // file.delete();
            log.info("Deleted local file copy");

            // Shut down client
            log.info("Quitting client");
            ftp.disconnect();

            log.info("Example complete");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

运行后服务器可看到正确的文件名, 而本机则可以下载到正确的文件副本.

输出日志为:

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.203 : Creating FTP client

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.359 : Connecting to server localhost

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.546 : Connected and logged in to server localhost

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.546 : Uploading file

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.703 : File uploaded

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.703 : Downloading file

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : File downloaded

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : Deleting remote file

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : Deleted remote file

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : Deleted local file copy

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : Quitting client

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : Example complete</p>

运行后一切正常, 非常好. 而此软件包的付费版本可支持目录批量上传和下载(Apache Commons Net 貌似不支持), 当然了, 许可证就要自己想办法Crack了:

import com.enterprisedt.net.ftp.FTPClient;
import com.enterprisedt.net.ftp.pro.ProFTPClient;
import com.enterprisedt.util.debug.Level;
import com.enterprisedt.util.debug.Logger;
import java.io.File;

public class TransferMultipleFilesDirectories {

    public static void main(String[] args) {

        // we want remote host, user name and password
        if (args.length < 5) {
            System.out
                    .println("Usage: run remote-host username password localdir remotedir");
            System.exit(1);
        }

        // extract command-line arguments
        String host = args[0];
        String username = args[1];
        String password = args[2];
        String localDir = args[3];
        String remoteDir = args[4];

        // set up logger so that we get some output
        Logger log = Logger.getLogger(TransferMultipleFilesDirectories.class);
        Logger.setLevel(Level.DEBUG);

        ProFTPClient ftp = null;

        try {
            // create client
            log.info("Creating FTP client");
            ftp = new ProFTPClient();

            // set remote host
            log.info("Setting remote host");
            ftp.setRemoteHost(host);

            // connect to the server
            log.info("Connecting to server " + host);
            ftp.connect();
            log.info("Connected to server " + host);

            // log in
            log.info("Logging in with username=" + username + " and password="
                    + password);
            ftp.login(username, password);
            log.info("Logged in");

            log.info("Uploading directory");
            ftp.mput(localDir, remoteDir, "*.html", true);
            log.info("Directory uploaded");

            log.info("Downloading directory");
            ftp.mget(localDir + ".copy", remoteDir, "*.html", true);
            log.info("Directory downloaded");

            log.info("Deleting remote directory");
            ftp.rmdir(remoteDir, true);
            log.info("Remote directory deleted");

            // Shut down client
            log.info("Quitting client");
            ftp.quit();

            log.info("Example complete");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

至此, 我们的任务已经完成, 可以加上定时器之类的软件或者类库实现定时同步/备份文件等功能. 想获取本项目源代码? 请点击 http://cid-519b3f7aa2172030.skydrive.live.com/self.aspx/java/opensource/javaftp.zip 138KB 下载(单线程下载, 请不要用下载软件如迅雷).

转载请注明:WebLogic Android 博客 » 用开源软件edtftpj实现FTP文件上传下载,无中文问题